Compact table markup (#2514)

* Compact table markup

* Motions tweak

* Small fixes
This commit is contained in:
Jaco Greeff 2020-04-04 08:15:36 +02:00 committed by GitHub
parent 491ec94801
commit b8df3dc8f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 588 additions and 646 deletions

View File

@ -116,8 +116,9 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
</>
)}
</Button.Group>
<Table>
<Table.Head filter={
<Table
empty={t('no accounts yet, create or import an existing')}
filter={
<div className='filter--tags'>
<Input
autoFocus
@ -127,30 +128,25 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
value={filter}
/>
</div>
}>
<th
className='start'
colSpan={3}
>
<h1>{t('accounts')}</h1>
</th>
<th>{t('type')}</th>
<th className='start'>{t('tags')}</th>
<th>{t('transactions')}</th>
<th>{t('balances')}</th>
<th colSpan={2}>&nbsp;</th>
</Table.Head>
<Table.Body empty={t('no accounts yet, create or import an existing')}>
{sortedAccounts.map(({ address, isFavorite }): React.ReactNode => (
<Account
address={address}
filter={filter}
isFavorite={isFavorite}
key={address}
toggleFavorite={toggleFavorite}
/>
))}
</Table.Body>
}
header={[
[t('accounts'), 'start', 3],
[t('type')],
[t('tags'), 'start'],
[t('transactions')],
[t('balances')],
[undefined, undefined, 2]
]}
>
{sortedAccounts.map(({ address, isFavorite }): React.ReactNode => (
<Account
address={address}
filter={filter}
isFavorite={isFavorite}
key={address}
toggleFavorite={toggleFavorite}
/>
))}
</Table>
</div>
);

View File

@ -54,8 +54,9 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
onStatusChange={onStatusChange}
/>
)}
<Table>
<Table.Head filter={
<Table
empty={t('no addresses saved yet, add any existing address')}
filter={
<div className='filter--tags'>
<Input
autoFocus
@ -65,29 +66,24 @@ function Overview ({ className, onStatusChange }: Props): React.ReactElement<Pro
value={filter}
/>
</div>
}>
<th
className='start'
colSpan={2}
>
<h1>{t('contacts')}</h1>
</th>
<th className='start'>{t('tags')}</th>
<th>{t('transactions')}</th>
<th>{t('balances')}</th>
<th colSpan={2}>&nbsp;</th>
</Table.Head>
<Table.Body empty={t('no addresses saved yet, add any existing address')}>
{sortedAddresses.map(({ address, isFavorite }): React.ReactNode => (
<Address
address={address}
filter={filter}
isFavorite={isFavorite}
key={address}
toggleFavorite={toggleFavorite}
/>
))}
</Table.Body>
}
header={[
[t('contacts'), 'start', 2],
[t('tags'), 'start'],
[t('transactions')],
[t('balances')],
[undefined, undefined, 2]
]}
>
{sortedAddresses.map(({ address, isFavorite }): React.ReactNode => (
<Address
address={address}
filter={filter}
isFavorite={isFavorite}
key={address}
toggleFavorite={toggleFavorite}
/>
))}
</Table>
</div>
);

View File

@ -44,26 +44,26 @@ function Proposals ({ className, motions, prime }: Props): React.ReactElement<Pr
members={members}
/>
</Button.Group>
<Table>
<Table.Head>
<th colSpan={2}>&nbsp;</th>
<th>{t('threshold')}</th>
<th>{t('voting end')}</th>
<th className='address'>{t('aye')}</th>
<th className='address'>{t('nay')}</th>
<th colSpan={2}>&nbsp;</th>
</Table.Head>
<Table.Body empty={motions && t('No council motions')}>
{motions?.map((motion: DeriveCollectiveProposal): React.ReactNode => (
<Motion
isMember={isMember}
key={motion.hash.toHex()}
members={members}
motion={motion}
prime={prime}
/>
))}
</Table.Body>
<Table
empty={motions && t('No council motions')}
header={[
[t('motions'), 'start', 2],
[t('threshold')],
[t('voting end')],
[t('aye'), 'address'],
[t('nay'), 'address'],
[undefined, undefined, 2]
]}
>
{motions?.map((motion: DeriveCollectiveProposal): React.ReactNode => (
<Motion
isMember={isMember}
key={motion.hash.toHex()}
members={members}
motion={motion}
prime={prime}
/>
))}
</Table>
</div>
);

View File

@ -21,46 +21,36 @@ function Candidates ({ allVotes = {}, electionsInfo }: Props): React.ReactElemen
return (
<>
<Table>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{t('runners up')}</h1>
</th>
<th>{t('backing')}</th>
</Table.Head>
<Table.Body empty={electionsInfo && t('No runners up found')}>
{electionsInfo?.runnersUp.map(([accountId, balance]): React.ReactNode => (
<Candidate
address={accountId}
balance={balance}
key={accountId.toString()}
voters={allVotes[accountId.toString()]}
/>
))}
</Table.Body>
<Table
empty={electionsInfo && t('No runners up found')}
header={[
[t('runners up'), 'start', 2],
[t('backing')]
]}
>
{electionsInfo?.runnersUp.map(([accountId, balance]): React.ReactNode => (
<Candidate
address={accountId}
balance={balance}
key={accountId.toString()}
voters={allVotes[accountId.toString()]}
/>
))}
</Table>
<Table>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{t('candidates')}</h1>
</th>
<th>{t('backing')}</th>
</Table.Head>
<Table.Body empty={electionsInfo && t('No candidates found')}>
{electionsInfo?.candidates.map((accountId): React.ReactNode => (
<Candidate
address={accountId}
key={accountId.toString()}
voters={allVotes[accountId.toString()]}
/>
))}
</Table.Body>
<Table
empty={electionsInfo && t('No candidates found')}
header={[
[t('candidates'), 'start', 2],
[t('backing')]
]}
>
{electionsInfo?.candidates.map((accountId): React.ReactNode => (
<Candidate
address={accountId}
key={accountId.toString()}
voters={allVotes[accountId.toString()]}
/>
))}
</Table>
</>
);

View File

@ -21,27 +21,23 @@ function Members ({ allVotes = {}, className, electionsInfo, prime }: Props): Re
const { t } = useTranslation();
return (
<Table className={className}>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{t('members')}</h1>
</th>
<th>{t('backing')}</th>
</Table.Head>
<Table.Body empty={electionsInfo && t('No members found')}>
{electionsInfo?.members.map(([accountId, balance]): React.ReactNode => (
<Candidate
address={accountId}
balance={balance}
isPrime={prime?.eq(accountId)}
key={accountId.toString()}
voters={allVotes[accountId.toString()]}
/>
))}
</Table.Body>
<Table
className={className}
empty={electionsInfo && t('No members found')}
header={[
[t('members'), 'start', 2],
[t('backing')]
]}
>
{electionsInfo?.members.map(([accountId, balance]): React.ReactNode => (
<Candidate
address={accountId}
balance={balance}
isPrime={prime?.eq(accountId)}
key={accountId.toString()}
voters={allVotes[accountId.toString()]}
/>
))}
</Table>
);
}

View File

@ -21,25 +21,21 @@ function DispatchQueue ({ className }: Props): React.ReactElement<Props> | null
const queued = useCall<DeriveDispatch[]>(api.derive.democracy.dispatchQueue as any, []);
return (
<Table className={className}>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{t('dispatch queue')}</h1>
</th>
<th>{t('enact')}</th>
<th colSpan={2}>&nbsp;</th>
</Table.Head>
<Table.Body empty={queued && t('Nothing queued for execution')}>
{queued?.map((entry): React.ReactNode => (
<DispatchEntry
key={entry.index.toString()}
value={entry}
/>
))}
</Table.Body>
<Table
className={className}
empty={queued && t('Nothing queued for execution')}
header={[
[t('dispatch queue'), 'start', 2],
[t('enact')],
[undefined, undefined, 2]
]}
>
{queued?.map((entry): React.ReactNode => (
<DispatchEntry
key={entry.index.toString()}
value={entry}
/>
))}
</Table>
);
}

View File

@ -21,19 +21,18 @@ function Externals ({ className }: Props): React.ReactElement<Props> | null {
const external = useCall<DeriveProposalExternal | null>(api.derive.democracy.nextExternal, []);
return (
<div className={`proposalSection ${className}`}>
<Table>
<Table.Head>
<th className='start'><h1>{t('external')}</h1></th>
<th className='address'>{t('proposer')}</th>
<th>{t('locked')}</th>
<th>&nbsp;</th>
</Table.Head>
<Table.Body empty={external === null && t('No external proposal')}>
{external && <External value={external} />}
</Table.Body>
</Table>
</div>
<Table
className={className}
empty={external === null && t('No external proposal')}
header={[
[t('external'), 'start'],
[t('proposer'), 'address'],
[t('locked')],
[]
]}
>
{external && <External value={external} />}
</Table>
);
}

View File

@ -21,26 +21,22 @@ function Proposals ({ className }: Props): React.ReactElement<Props> {
const proposals = useCall<DeriveProposal[]>(api.derive.democracy.proposals, []);
return (
<Table className={className}>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{t('proposals')}</h1>
</th>
<th className='address'>{t('proposer')}</th>
<th>{t('locked')}</th>
<th colSpan={3}>&nbsp;</th>
</Table.Head>
<Table.Body empty={proposals && t('No active proposals')}>
{proposals?.map((proposal): React.ReactNode => (
<ProposalDisplay
key={proposal.index.toString()}
value={proposal}
/>
))}
</Table.Body>
<Table
className={className}
empty={proposals && t('No active proposals')}
header={[
[t('proposals'), 'start', 2],
[t('proposer'), 'address'],
[t('locked')],
[undefined, undefined, 3]
]}
>
{proposals?.map((proposal): React.ReactNode => (
<ProposalDisplay
key={proposal.index.toString()}
value={proposal}
/>
))}
</Table>
);
}

View File

@ -21,28 +21,24 @@ function Referendums ({ className }: Props): React.ReactElement<Props> {
const referendums = useCall<DeriveReferendumExt[]>(api.derive.democracy.referendums, []);
return (
<Table className={className}>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{t('referenda')}</h1>
</th>
<th>{t('remaining')}</th>
<th>{t('activate')}</th>
<th>{t('aye')}</th>
<th>{t('nay')}</th>
<th colSpan={3}>&nbsp;</th>
</Table.Head>
<Table.Body empty={referendums && t('No active referendums')}>
{referendums?.map((referendum): React.ReactNode => (
<Referendum
key={referendum.index.toString()}
value={referendum}
/>
))}
</Table.Body>
<Table
className={className}
empty={referendums && t('No active referendums')}
header={[
[t('referenda'), 'start', 2],
[t('remaining')],
[t('activate')],
[t('aye')],
[t('nay')],
[undefined, undefined, 3]
]}
>
{referendums?.map((referendum): React.ReactNode => (
<Referendum
key={referendum.index.toString()}
value={referendum}
/>
))}
</Table>
);
}

View File

@ -17,25 +17,20 @@ function BlockHeaders ({ headers }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
return (
<Table>
<Table.Head>
<th
className='start'
colSpan={3}
>
<h1>{t('recent blocks')}</h1>
</th>
</Table.Head>
<Table.Body empty={t('No blocks available')}>
{headers
.filter((header) => !!header)
.map((header): React.ReactNode => (
<BlockHeader
key={header.number.toString()}
value={header}
/>
))}
</Table.Body>
<Table
empty={t('No blocks available')}
header={[
[t('recent blocks'), 'start', 3]
]}
>
{headers
.filter((header) => !!header)
.map((header): React.ReactNode => (
<BlockHeader
key={header.number.toString()}
value={header}
/>
))}
</Table>
);
}

View File

@ -33,41 +33,36 @@ function BlockByHash ({ className, value }: Props): React.ReactElement<Props> {
return (
<div className={className}>
<Table isFixed>
<Table.Head>
<th
className='start'
colSpan={getHeader ? 1 : 6}
>
<h1>{blockNumber ? formatNumber(blockNumber) : '...'}</h1>
</th>
{getHeader && (
<>
<th className='start'>{t('hash')}</th>
<th className='start'>{t('parent')}</th>
<th className='start'>{t('extrinsics')}</th>
<th className='start'>{t('state')}</th>
<th>&nbsp;</th>
</>
)}
</Table.Head>
<Table.Body>
{getBlock && !getBlock.isEmpty && getHeader && !getHeader.isEmpty && (
<tr>
<td className='address'>{getHeader.author && <AddressMini value={getHeader.author} />}</td>
<td className='hash overflow'>{getHeader.hash.toHex()}</td>
<td className='hash overflow'><Link to={`/explorer/query/${parentHash}`}>{parentHash}</Link></td>
<td className='hash overflow'>{getHeader.extrinsicsRoot.toHex()}</td>
<td className='hash overflow'>{getHeader.stateRoot.toHex()}</td>
<td>
<LinkExternal
data={value}
type='block'
/>
</td>
</tr>
)}
</Table.Body>
<Table
header={
getHeader
? [
[formatNumber(blockNumber), 'start', 1],
[t('hash'), 'start'],
[t('parent'), 'start'],
[t('extrinsics'), 'start'],
[t('state'), 'start'],
[]
]
: [['...', 'start', 6]]
}
isFixed
>
{getBlock && !getBlock.isEmpty && getHeader && !getHeader.isEmpty && (
<tr>
<td className='address'>{getHeader.author && <AddressMini value={getHeader.author} />}</td>
<td className='hash overflow'>{getHeader.hash.toHex()}</td>
<td className='hash overflow'><Link to={`/explorer/query/${parentHash}`}>{parentHash}</Link></td>
<td className='hash overflow'>{getHeader.extrinsicsRoot.toHex()}</td>
<td className='hash overflow'>{getHeader.stateRoot.toHex()}</td>
<td>
<LinkExternal
data={value}
type='block'
/>
</td>
</tr>
)}
</Table>
{getBlock && getHeader && (
<>

View File

@ -24,34 +24,23 @@ function Extrinsics ({ blockNumber, className, events, label, value }: Props): R
return (
<Table
className={className}
empty={t('No pending extrinsics are in the queue')}
header={[
[label || t('extrinsics'), 'start', 2],
[t('events'), 'start', 2],
[t('signer'), 'address']
]}
isFixed
>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{label || t('extrinsics')}</h1>
</th>
<th
className='start'
colSpan={2}
>
{t('events')}
</th>
<th className='address'>{t('signer')}</th>
</Table.Head>
<Table.Body empty={t('No pending extrinsics are in the queue')}>
{value?.map((extrinsic, index): React.ReactNode =>
<ExtrinsicDisplay
blockNumber={blockNumber}
events={events}
index={index}
key={`extrinsic:${index}`}
value={extrinsic}
/>
)}
</Table.Body>
{value?.map((extrinsic, index): React.ReactNode =>
<ExtrinsicDisplay
blockNumber={blockNumber}
events={events}
index={index}
key={`extrinsic:${index}`}
value={extrinsic}
/>
)}
</Table>
);
}

View File

@ -104,21 +104,19 @@ function Logs (props: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
return (
<Table>
<Table.Head>
<th className='start'><h1>{t('logs')}</h1></th>
</Table.Head>
<Table.Body>
{value?.map((log, index) => (
<tr key={`log:${index}`}>
<td className='overflow'>
<Expander summary={log.type.toString()}>
{formatItem(log)}
</Expander>
</td>
</tr>
))}
</Table.Body>
<Table
empty={t('No logs available')}
header={[[t('logs'), 'start']]}
>
{value?.map((log, index) => (
<tr key={`log:${index}`}>
<td className='overflow'>
<Expander summary={log.type.toString()}>
{formatItem(log)}
</Expander>
</td>
</tr>
))}
</Table>
);
}

View File

@ -21,25 +21,25 @@ function Events ({ emptyLabel, eventClassName, events, label }: Props): React.Re
const { t } = useTranslation();
return (
<Table>
<Table.Head>
<th className='start'><h1>{label || t('recent events')}</h1></th>
</Table.Head>
<Table.Body empty={emptyLabel || t('No events available')}>
{events
.filter(({ event: { method, section } }): boolean => !!method && !!section)
.map((event: EventRecord, index): React.ReactNode => (
<tr
className={eventClassName}
key={`event:${index}`}
>
<td className='overflow'>
<Event value={event} />
</td>
</tr>
))
}
</Table.Body>
<Table
empty={emptyLabel || t('No events available')}
header={[
[label || t('recent events'), 'start']
]}
>
{events
.filter(({ event: { method, section } }): boolean => !!method && !!section)
.map((event: EventRecord, index): React.ReactNode => (
<tr
className={eventClassName}
key={`event:${index}`}
>
<td className='overflow'>
<Event value={event} />
</td>
</tr>
))
}
</Table>
);
}

View File

@ -19,23 +19,24 @@ function Peers ({ className, peers }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
return (
<Table className={className}>
<Table.Head>
<th className='start'><h1>{t('connected peers')}</h1></th>
<th className='start'>{t('role')}</th>
<th className='number'>{t('best #')}</th>
<th className='start'>{t('best hash')}</th>
</Table.Head>
<Table.Body empty={t('no peers connected')}>
{peers?.sort((a, b): number => b.bestNumber.cmp(a.bestNumber)).map((peer) => (
<tr key={peer.peerId.toString()}>
<td className='hash'>{peer.peerId.toString()}</td>
<td>{peer.roles.toString().toLowerCase()}</td>
<td className='number all'>{formatNumber(peer.bestNumber)}</td>
<td className='hash'>{peer.bestHash.toHex()}</td>
</tr>
))}
</Table.Body>
<Table
className={className}
empty={t('no peers connected')}
header={[
[t('connected peers'), 'start'],
[t('role'), 'start'],
[t('best #'), 'number'],
[t('best hash'), 'hash']
]}
>
{peers?.sort((a, b): number => b.bestNumber.cmp(a.bestNumber)).map((peer) => (
<tr key={peer.peerId.toString()}>
<td className='hash'>{peer.peerId.toString()}</td>
<td>{peer.roles.toString().toLowerCase()}</td>
<td className='number all'>{formatNumber(peer.bestNumber)}</td>
<td className='hash'>{peer.bestHash.toHex()}</td>
</tr>
))}
</Table>
);
}

View File

@ -19,28 +19,25 @@ function Parachains ({ parachains }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
return (
<Table>
<Table.Head>
<th colSpan={4}>&nbsp;</th>
<th>{t('swap to id')}</th>
<th>{t('scheduling')}</th>
</Table.Head>
<Table.Body>
{parachains.map((parachain): React.ReactNode => (
<Parachain
key={parachain.id.toString()}
parachain={parachain}
/>
))}
</Table.Body>
<Table
header={[
[t('parachains'), 'start', 4],
[t('swap to id')],
[t('scheduling')]
]}
>
{parachains.map((parachain): React.ReactNode => (
<Parachain
key={parachain.id.toString()}
parachain={parachain}
/>
))}
</Table>
);
}
export default React.memo(styled(Parachains)`
tbody {
tr {
cursor: pointer;
}
tbody tr {
cursor: pointer;
}
`);

View File

@ -21,20 +21,21 @@ function Bids ({ className }: Props): React.ReactElement<Props> {
const bids = useCall<Bid[]>(api.query.society.bids, []);
return (
<Table className={className}>
<Table.Head>
<th className='start'><h1>{t('bids')}</h1></th>
<th>{t('kind')}</th>
<th>{t('value')}</th>
</Table.Head>
<Table.Body empty={bids && t('No bids')}>
{bids?.map((bid): React.ReactNode => (
<BidRow
key={bid.who.toString()}
value={bid}
/>
))}
</Table.Body>
<Table
className={className}
empty={bids && t('No bids')}
header={[
[t('bids'), 'start'],
[t('kind')],
[t('value')]
]}
>
{bids?.map((bid): React.ReactNode => (
<BidRow
key={bid.who.toString()}
value={bid}
/>
))}
</Table>
);
}

View File

@ -22,25 +22,25 @@ function Candidates ({ allMembers, className, isMember, ownMembers }: Props): Re
const candidates = useCall<DeriveSocietyCandidate[]>(api.derive.society.candidates, []);
return (
<Table className={className}>
<Table.Head>
<th className='start'><h1>{t('candidates')}</h1></th>
<th>{t('kind')}</th>
<th>{t('value')}</th>
<th className='start'>{t('votes')}</th>
<th>&nbsp;</th>
</Table.Head>
<Table.Body empty={candidates && t('No candidates')}>
{candidates?.map((candidate): React.ReactNode => (
<Candidate
allMembers={allMembers}
isMember={isMember}
key={candidate.accountId.toString()}
ownMembers={ownMembers}
value={candidate}
/>
))}
</Table.Body>
<Table
className={className}
empty={candidates && t('No candidates')}
header={[
[t('candidates'), 'start'],
[t('kind')],
[t('value')],
[t('votes'), 'start']
]}
>
{candidates?.map((candidate): React.ReactNode => (
<Candidate
allMembers={allMembers}
isMember={isMember}
key={candidate.accountId.toString()}
ownMembers={ownMembers}
value={candidate}
/>
))}
</Table>
);
}

View File

@ -34,26 +34,26 @@ function Defender ({ className, info, isMember, ownMembers }: Props): React.Reac
}
return (
<Table className={className}>
<Table.Head>
<th className='start'><h1>{t('defender')}</h1></th>
<th className='start'>{t('votes')}</th>
<th>&nbsp;</th>
</Table.Head>
<Table.Body>
<tr>
<td className='address all'>
<AddressSmall value={info.defender} />
</td>
<Votes votes={votes} />
<td className='button'>
<DefenderVoting
isMember={isMember}
ownMembers={ownMembers}
/>
</td>
</tr>
</Table.Body>
<Table
className={className}
header={[
[t('defender'), 'start'],
[t('votes'), 'start'],
[]
]}
>
<tr>
<td className='address all'>
<AddressSmall value={info.defender} />
</td>
<Votes votes={votes} />
<td className='button'>
<DefenderVoting
isMember={isMember}
ownMembers={ownMembers}
/>
</td>
</tr>
</Table>
);
}

View File

@ -29,25 +29,21 @@ function Members ({ className, info }: Props): React.ReactElement<Props> {
}, [info, members]);
return (
<Table className={className}>
<Table.Head>
<th
className='start'
colSpan={3}
>
<h1>{t('members')}</h1>
</th>
<th>{t('strikes')}</th>
</Table.Head>
<Table.Body empty={info && t('No active members')}>
{filtered.map((member): React.ReactNode => (
<Member
isHead={info?.head?.eq(member.accountId)}
key={member.accountId.toString()}
value={member}
/>
))}
</Table.Body>
<Table
className={className}
empty={info && t('No active members')}
header={[
[t('members'), 'start', 3],
[t('strikes')]
]}
>
{filtered.map((member): React.ReactNode => (
<Member
isHead={info?.head?.eq(member.accountId)}
key={member.accountId.toString()}
value={member}
/>
))}
</Table>
);
}

View File

@ -77,35 +77,30 @@ function Actions ({ allRewards, allStashes, className, isVisible, next, stakingO
{isNewStakeOpen && (
<StartStaking onClose={_toggleNewStake} />
)}
<Table>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{t('stashes')}</h1>
</th>
<th className='address'>{t('controller')}</th>
<th className='number'>{t('rewards')}</th>
<th className='number'>{t('bonded')}</th>
<th colSpan={2}>&nbsp;</th>
</Table.Head>
<Table.Body empty={t('No funds staked yet. Bond funds to validate or nominate a validator.')}>
{foundStashes?.map(([stashId, isOwnStash]): React.ReactNode => (
<Account
activeEra={activeEra}
allStashes={allStashes}
isOwnStash={isOwnStash}
isVisible={isVisible}
key={stashId}
next={next}
onUpdateType={_onUpdateType}
rewards={allRewards && allRewards[stashId]}
stakingOverview={stakingOverview}
stashId={stashId}
/>
))}
</Table.Body>
<Table
empty={t('No funds staked yet. Bond funds to validate or nominate a validator')}
header={[
[t('stashes'), 'start', 2],
[t('controller'), 'address'],
[t('rewards'), 'number'],
[t('bonded'), 'number'],
[undefined, undefined, 2]
]}
>
{foundStashes?.map(([stashId, isOwnStash]): React.ReactNode => (
<Account
activeEra={activeEra}
allStashes={allStashes}
isOwnStash={isOwnStash}
isVisible={isVisible}
key={stashId}
next={next}
onUpdateType={_onUpdateType}
rewards={allRewards && allRewards[stashId]}
stakingOverview={stakingOverview}
stashId={stashId}
/>
))}
</Table>
</div>
);

View File

@ -136,24 +136,19 @@ function CurrentList ({ authorsMap, hasQueries, isIntentions, lastAuthors, next,
return isIntentions
? (
<Table>
<Table.Head>
<th
className='start'
colSpan={9}
>
<h1>{t('intentions')}</h1>
</th>
</Table.Head>
<Table.Body empty={waiting && t('No waiting validators found')}>
{_renderRows(elected, false).concat(..._renderRows(waiting, false))}
{}
</Table.Body>
<Table
empty={waiting && t('No waiting validators found')}
header={[
[t('intentions'), 'start', 9]
]}
>
{_renderRows(elected, false).concat(..._renderRows(waiting, false))}
</Table>
)
: (
<Table>
<Table.Head filter={
<Table
empty={validators && t('No active validators found')}
filter={
<Input
autoFocus
isFull
@ -161,23 +156,18 @@ function CurrentList ({ authorsMap, hasQueries, isIntentions, lastAuthors, next,
onChange={setNameFilter}
value={nameFilter}
/>
}>
<th
className='start'
colSpan={3}
>
<h1>{t('validators')}</h1>
</th>
<th>{t('other stake')}</th>
<th>{t('own stake')}</th>
<th>{t('commission')}</th>
<th>{t('points')}</th>
<th>{t('last #')}</th>
<th>&nbsp;</th>
</Table.Head>
<Table.Body empty={validators && t('No active validators found')}>
{_renderRows(validators, true)}
</Table.Body>
}
header={[
[t('validators'), 'start', 3],
[t('other stake')],
[t('own stake')],
[t('commission')],
[t('points')],
[t('last #')],
[]
]}
>
{_renderRows(validators, true)}
</Table>
);
}

View File

@ -219,8 +219,9 @@ function Targets ({ className }: Props): React.ReactElement<Props> {
numValidators={validators.length}
totalStaked={totalStaked}
/>
<Table>
<Table.Head filter={
<Table
empty={sorted && t('No active validators to check for rewards available')}
filter={
<InputBalance
className='balanceInput'
help={t('The amount that will be used on a per-validator basis to calculate rewards for that validator.')}
@ -229,31 +230,25 @@ function Targets ({ className }: Props): React.ReactElement<Props> {
onChange={setAmount}
value={_amount}
/>
}>
<th
className='start'
colSpan={3}
>
<h1>{t('validators')}</h1>
</th>
{['rankComm', 'rankBondTotal', 'rankBondOwn', 'rankBondOther', 'rankOverall'].map((header): React.ReactNode => (
<th
className={`isClickable ${sortBy === header && 'ui--highlight--border'} number`}
key={header}
onClick={(): void => _sort(header as 'rankComm')}
>{labels[header]}<Icon name={sortBy === header ? (sortFromMax ? 'chevron down' : 'chevron up') : 'minus'} /></th>
))}
<th>&nbsp;</th>
</Table.Head>
<Table.Body empty={sorted && t('No active validators to check for rewards available')}>
{sorted?.map((info): React.ReactNode =>
<Validator
info={info}
key={info.key}
toggleFavorite={toggleFavorite}
/>
)}
</Table.Body>
}
header={[
[t('validators'), 'start', 3],
...['rankComm', 'rankBondTotal', 'rankBondOwn', 'rankBondOther', 'rankOverall'].map((header) => [
<>{labels[header]}<Icon name={sortBy === header ? (sortFromMax ? 'chevron down' : 'chevron up') : 'minus'} /></>,
`isClickable ${sortBy === header && 'ui--highlight--border'} number`,
1,
(): void => _sort(header as 'rankComm')
]),
[]
]}
>
{sorted?.map((info): React.ReactNode =>
<Validator
info={info}
key={info.key}
toggleFavorite={toggleFavorite}
/>
)}
</Table>
</div>
);

View File

@ -19,34 +19,30 @@ function Members ({ className, members, prime }: Props): React.ReactElement<Prop
const { t } = useTranslation();
return (
<Table className={className}>
<Table.Head>
<th
className='start'
colSpan={3}
>
<h1>{t('members')}</h1>
</th>
</Table.Head>
<Table.Body empty={members && t('No members found')}>
{members?.map((accountId): React.ReactNode => (
<tr key={accountId.toString()}>
<td className='address'>
<AddressSmall value={accountId} />
</td>
<td>
{prime?.eq(accountId) && (
<Tag
color='green'
hover={t('Committee prime member, default voting')}
label={t('prime member')}
/>
)}
</td>
<td className='all'>&nbsp;</td>
</tr>
))}
</Table.Body>
<Table
className={className}
empty={members && t('No members found')}
header={[
[t('members'), 'start', 3]
]}
>
{members?.map((accountId): React.ReactNode => (
<tr key={accountId.toString()}>
<td className='address'>
<AddressSmall value={accountId} />
</td>
<td>
{prime?.eq(accountId) && (
<Tag
color='green'
hover={t('Committee prime member, default voting')}
label={t('prime member')}
/>
)}
</td>
<td className='all'>&nbsp;</td>
</tr>
))}
</Table>
);
}

View File

@ -23,28 +23,23 @@ function Proposals ({ className, isMember, members, prime, proposals }: Props):
members={members}
/>
</Button.Group>
<Table>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{t('proposals')}</h1>
</th>
<th>{t('threshold')}</th>
<th className='address'>{t('aye')}</th>
<th className='address'>{t('nay')}</th>
<th>&nbsp;</th>
</Table.Head>
<Table.Body empty={proposals && t('No committee proposals')}>
{proposals?.map((hash: Hash): React.ReactNode => (
<Proposal
imageHash={hash.toHex()}
key={hash.toHex()}
prime={prime}
/>
))}
</Table.Body>
<Table
empty={proposals && t('No committee proposals')}
header={[
[t('proposals'), 'start', 2],
[t('threshold')],
[t('aye'), 'address'],
[t('nay'), 'address'],
[]
]}
>
{proposals?.map((hash: Hash): React.ReactNode => (
<Proposal
imageHash={hash.toHex()}
key={hash.toHex()}
prime={prime}
/>
))}
</Table>
</div>
);

View File

@ -40,30 +40,26 @@ function ProposalsBase ({ className, isApprovals, proposals }: Props): React.Rea
};
return (
<Table className={className}>
<Table.Head>
<th
className='start'
colSpan={2}
>
<h1>{isApprovals ? t('Approved') : t('Proposals')}</h1>
</th>
<th className='address'>{t('beneficiary')}</th>
<th>{t('payment')}</th>
<th>{t('bond')}</th>
<th colSpan={2}>&nbsp;</th>
</Table.Head>
<Table.Body empty={proposals && (isApprovals ? t('No approved proposals') : t('No pending proposals'))}>
{proposals?.map((proposal): React.ReactNode => (
<Proposal
isMember={isMember}
key={proposal.id.toString()}
onRespond={_onRespond}
proposal={proposal}
withSend={!isApprovals}
/>
))}
</Table.Body>
<Table
className={className}
empty={proposals && (isApprovals ? t('No approved proposals') : t('No pending proposals'))}
header={[
[isApprovals ? t('Approved') : t('Proposals'), 'start', 2],
[t('beneficiary'), 'address'],
[t('payment')],
[t('bond')],
[undefined, undefined, 2]
]}
>
{proposals?.map((proposal): React.ReactNode => (
<Proposal
isMember={isMember}
key={proposal.id.toString()}
onRespond={_onRespond}
proposal={proposal}
withSend={!isApprovals}
/>
))}
</Table>
);
}

View File

@ -0,0 +1,34 @@
// Copyright 2017-2020 @polkadot/react-components 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 React from 'react';
import { isString } from '@polkadot/util';
import Spinner from '../Spinner';
interface Props {
children?: React.ReactNode;
className?: string;
empty?: React.ReactNode;
isEmpty?: boolean;
}
function Body ({ children, className, empty, isEmpty }: Props): React.ReactElement<Props> {
return (
<tbody className={className}>
{isEmpty
? (
<tr><td colSpan={100}>{
isString(empty)
? <div className='empty'>{empty}</div>
: empty || <Spinner />
}</td></tr>
)
: children
}
</tbody>
);
}
export default React.memo(Body);

View File

@ -0,0 +1,44 @@
// Copyright 2017-2020 @polkadot/react-components 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 React from 'react';
interface Props {
className?: string;
filter?: React.ReactNode;
header: [React.ReactNode?, string?, number?, (() => void)?][];
isEmpty: boolean;
}
function Head ({ className, filter, header, isEmpty }: Props): React.ReactElement<Props> {
return (
<thead className={className}>
{filter && (
<tr className='filter'>
<th colSpan={100}>{filter}</th>
</tr>
)}
<tr>
{header.map(([label, className = 'default', colSpan = 1, onClick], index) =>
<th
className={className}
colSpan={colSpan}
key={index}
onClick={onClick}
>
{
index === 0
? <h1>{label}</h1>
: isEmpty
? ''
: label
}
</th>
)}
</tr>
</thead>
);
}
export default React.memo(Head);

View File

@ -4,77 +4,42 @@
import React from 'react';
import styled from 'styled-components';
import { isString } from '@polkadot/util';
import Spinner from './Spinner';
import Body from './Body';
import Head from './Head';
interface BaseProps {
interface TableProps {
children: React.ReactNode;
className?: string;
}
interface BodyProps extends BaseProps {
empty?: React.ReactNode;
}
interface HeadProps extends BaseProps {
filter?: React.ReactNode;
}
interface TableProps extends BaseProps {
header: [React.ReactNode?, string?, number?, (() => void)?][];
isFixed?: boolean;
}
type TableImpl = React.FC<TableProps> & {
Body: React.FC<BodyProps>;
Head: React.FC<HeadProps>;
}
function Head ({ children, className, filter }: HeadProps): React.ReactElement<HeadProps> {
return (
<thead className={className}>
{filter && (
<tr className='filter'>
<th colSpan={100}>{filter}</th>
</tr>
)}
<tr>
{children}
</tr>
</thead>
);
}
function Body ({ children, className, empty }: BodyProps): React.ReactElement<BodyProps> {
function Table ({ children, className, empty, filter, header, isFixed }: TableProps): React.ReactElement<TableProps> {
const isEmpty = !children || (Array.isArray(children) && children.length === 0);
return (
<tbody className={className}>
{isEmpty
? (
<tr><td colSpan={100}>{
isString(empty)
? <div className='empty'>{empty}</div>
: empty || <Spinner />
}</td></tr>
)
: children
}
</tbody>
);
}
function Table ({ children, className, isFixed }: TableProps): React.ReactElement<TableProps> {
return (
<div className={`ui--Table ${isFixed && 'isFixed'} ${className}`}>
<table>
{children}
<Head
filter={filter}
header={header}
isEmpty={isEmpty}
/>
<Body
empty={empty}
isEmpty={isEmpty}
>
{children}
</Body>
</table>
</div>
);
}
const Memo = React.memo(styled(Table)`
export default React.memo(styled(Table)`
margin-bottom: 1.5rem;
max-width: 100%;
width: 100%;
@ -293,9 +258,4 @@ const Memo = React.memo(styled(Table)`
}
}
}
`) as unknown as TableImpl;
Memo.Body = React.memo(Body);
Memo.Head = React.memo(Head);
export default Memo;
`);