Skip to content

Commit

Permalink
Merge pull request #322 from 1Hive/accordion-to-collapsable-blocks
Browse files Browse the repository at this point in the history
Accordion to collapsable blocks
  • Loading branch information
Corantin committed Jun 17, 2022
2 parents c9c9b2c + 9a149e3 commit bb44e1d
Show file tree
Hide file tree
Showing 13 changed files with 241 additions and 128 deletions.
4 changes: 3 additions & 1 deletion packages/react-app/src/components/account/account-module.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Button, GU, IconConnect, springs } from '@1hive/1hive-ui';
import { noop } from 'lodash-es';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { animated, Transition } from 'react-spring/renderprops';
import { getNetwork } from 'src/networks';
import styled from 'styled-components';
import { useWallet } from '../../contexts/wallet.context';
import { getUseWalletProviders, isConnected } from '../../utils/web3.utils';
Expand Down Expand Up @@ -58,6 +59,7 @@ type Props = {
function AccountModule({ compact = false }: Props) {
const buttonRef = useRef<any>();
const wallet = useWallet();
const { name } = getNetwork();
const { walletAddress, activatingId, deactivateWallet, activateWallet } = wallet;
const [opened, setOpened] = useState(false);
const [animate, setAnimate] = useState(false);
Expand Down Expand Up @@ -161,7 +163,7 @@ function AccountModule({ compact = false }: Props) {
{wallet.isWrongNetwork ? (
<Button
icon={<IconConnect />}
label="Switch wallet network"
label={`Switch wallet to ${name}`}
onClick={() => wallet.changeNetwork()}
display={compact ? 'icon' : 'all'}
mode="strong"
Expand Down
134 changes: 84 additions & 50 deletions packages/react-app/src/components/claim-list.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { Accordion, Box } from '@1hive/1hive-ui';
import { ClaimModel } from 'src/models/claim.model';
import styled from 'styled-components';
import { GUpx } from 'src/utils/style.util';
import { ENUM_TRANSACTION_STATUS } from 'src/constants';
import { TokenAmountModel } from 'src/models/token-amount.model';
import { QuestModel } from 'src/models/quest.model';
import { useEffect, useMemo, useState } from 'react';
import { Fragment, useEffect, useMemo, useState } from 'react';
import { useTransactionContext } from 'src/contexts/transaction.context';
import { useIsMountedRef } from 'src/hooks/use-mounted.hook';
import { noop } from 'lodash';
import { ThemeInterface } from 'src/styles/theme';
import { useThemeContext } from 'src/contexts/theme.context';
import { HelpTooltip } from './field-input/help-tooltip';
import * as QuestService from '../services/quest.service';
import Claim from './claim';
import TextFieldInput from './field-input/text-field-input';
import { CollapsableBlock } from './collapsable-block';

// #region StyledComponents

Expand All @@ -33,15 +35,30 @@ const HeaderStyled = styled.h1`
margin-left: ${GUpx(1)};
`;

const BoxStyled = styled(Box)`
const ClaimsWrapperStyled = styled.div`
display: flex;
flex-direction: column;
justify-content: space-around;
`;

const EvidenceWrapperStyled = styled.div`
padding: ${GUpx(2)};
`;

const NoClaimBoxStyled = styled.div<{ theme: ThemeInterface }>`
background: ${({ theme }: any) => theme.surfaceUnder} !important;
padding: ${GUpx(2)};
border-radius: 8px;
width: 95%;
margin-left: ${GUpx(2)};
margin-top: ${GUpx(2)};
box-shadow: 10px 10px 15px 5px #00000029;
display: flex;
align-items: center;
justify-content: center;
height: 100px;
`;

// #endregion

type Props = {
Expand All @@ -61,52 +78,31 @@ export default function ClaimList({
const [loadingClaim, setLoadingClaim] = useState(true);
const { transaction } = useTransactionContext();
const isMountedRef = useIsMountedRef();
const { currentTheme } = useThemeContext();

const skeletonClaim = useMemo(() => {
const fakeClaim = {} as ClaimModel;
return [
<Claim
isLoading
claim={fakeClaim}
questData={questData}
challengeDeposit={challengeDeposit}
/>,
<EvidenceWrapperStyled>
<TextFieldInput id="evidence" isLoading />
</EvidenceWrapperStyled>,
];
}, []);

const accordionItems = useMemo(() => {
const items = claims.map((claim: ClaimModel) => [
<Claim
claim={claim}
challengeDeposit={challengeDeposit}
questData={questData}
isLoading={isLoading}
/>,
<EvidenceWrapperStyled>
<TextFieldInput
id="evidence"
value={claim.evidence}
isMarkDown
wide
label="Evidence of completion"
/>
{claim.contactInformation && (
<TextFieldInput
id="contact"
value={claim.contactInformation}
wide
label="Contact information"
return (
<CollapsableBlock
hideState
visible
copyable={false}
collapsed
header={
<Claim
isLoading
claim={fakeClaim}
questData={questData}
challengeDeposit={challengeDeposit}
/>
)}
</EvidenceWrapperStyled>,
]);
if (loadingClaim) {
items.unshift(skeletonClaim);
}
return items;
}, [claims, loadingClaim, questData.bounty, isLoading, challengeDeposit]);
}
>
<EvidenceWrapperStyled>
<TextFieldInput id="evidence" isLoading />
</EvidenceWrapperStyled>
</CollapsableBlock>
);
}, []);

useEffect(() => {
if (questData.address) {
Expand Down Expand Up @@ -161,11 +157,49 @@ export default function ClaimList({
<HelpTooltip tooltip="A claim includes the proof of the quest's completion." />
</ClaimHeaderStyled>
{claims?.length || loadingClaim ? (
<Accordion items={accordionItems} className="accordion-fit-content" />
<ClaimsWrapperStyled>
{loadingClaim && skeletonClaim}
{claims.map((claim) => (
<Fragment key={claim.container?.id}>
<CollapsableBlock
hideState
visible
copyable={false}
collapsed
header={
<Claim
claim={claim}
challengeDeposit={challengeDeposit}
questData={questData}
isLoading={isLoading}
/>
}
>
<EvidenceWrapperStyled>
<TextFieldInput
id="evidence"
value={claim.evidence}
isMarkDown
wide
label="Evidence of completion"
/>
{claim.contactInformation && (
<TextFieldInput
id="contact"
value={claim.contactInformation}
wide
label="Contact information"
/>
)}
</EvidenceWrapperStyled>
</CollapsableBlock>
</Fragment>
))}
</ClaimsWrapperStyled>
) : (
<BoxStyled>
<i>No claims</i>
</BoxStyled>
<ClaimsWrapperStyled>
<NoClaimBoxStyled theme={currentTheme}>No claims</NoClaimBoxStyled>
</ClaimsWrapperStyled>
)}
</WrapperStyled>
);
Expand Down
2 changes: 1 addition & 1 deletion packages/react-app/src/components/claim.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export default function Claim({ claim, isLoading, challengeDeposit, questData }:
setActionButton(
<>
<ChallengeModal
claim={claim}
claim={{ ...claim, state }}
challengeDeposit={challengeDeposit}
onClose={onActionClose}
/>
Expand Down
124 changes: 82 additions & 42 deletions packages/react-app/src/components/collapsable-block.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
/* eslint-disable react/destructuring-assignment */
import { Button, IconDown, IconUp, IconCopy, useTheme } from '@1hive/1hive-ui';
import { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { ReactNode, useEffect, useMemo, useState } from 'react';
import { ThemeInterface } from 'src/styles/theme';
import styled, { css } from 'styled-components';
import { useCopyToClipboard } from '../hooks/use-copy-to-clipboard.hook';
import { GUpx } from '../utils/style.util';
import { ConditionalWrapper } from './utils/util';

// #region StyledComponents

const IconColumnStyled = styled.div`
display: flex;
flex-direction: column;
margin-right: ${GUpx(1)};
padding: ${GUpx(1)};
`;

const CollapseButtonStyled = styled.a`
const CollapseButtonStyled = styled(Button)`
display: flex;
cursor: pointer;
text-decoration: none !important;
user-select: none;
flex-grow: 1;
height: fit-content;
padding: ${GUpx(1)};
`;

const CopyButtonStyled = styled(Button)`
Expand All @@ -30,32 +34,61 @@ const LineStyled = styled.div`
justify-content: space-between;
align-items: center;
width: 100%;
height: fit-content;
`;

const ContentWrapperStyled = styled.div`
padding-top: ${GUpx(1)};
`;

const LabelStyled = styled.span`
height: 100%;
color: ${({ theme }: any) => theme.link} !important;
const WrapperStyled = styled.div<{ usePre: boolean | undefined; wide: boolean | undefined }>`
${({ usePre, wide }) =>
css`
${usePre ? 'pre {' : ''}
background: ${({ theme }: any) => theme.surfaceUnder} !important;
padding: ${GUpx(2)};
border-radius: 8px;
width: ${wide ? '100%' : '95%'};
margin-left: ${wide ? '0' : GUpx(2)};
margin-top: ${GUpx(2)};
box-shadow: 10px 10px 15px 5px #00000029;
${usePre ? '}' : ''}
`}
margin-bottom: ${GUpx(1)};
a {
color: ${({ theme }: any) => theme.link};
}
`;

const HeaderWraperStyled = styled.div`
flex-grow: 1;
padding-left: 16px;
`;

const WrapperStyled = styled.pre`
background: ${({ theme }: any) => theme.surfaceUnder} !important;
const CollapseButtonContentStyled = styled.div<{ theme: ThemeInterface }>`
color: ${({ theme }: any) => theme.link} !important;
display: flex;
align-items: center;
width: 100%;
`;

// #endregion

type Props = {
children: any;
label?: string;
header?: ReactNode;
visible?: boolean;
src?: string;
width?: number;
collapsed?: boolean;
type?: 'image' | 'code' | 'default';
alt?: string;
copyable?: boolean;
hideState?: boolean;
usePre?: boolean;
wide?: boolean;
};

export function CollapsableBlock(props: Props) {
Expand All @@ -76,38 +109,45 @@ export function CollapsableBlock(props: Props) {
return (
<>
{props.visible && (
<WrapperStyled theme={theme}>
<LineStyled>
<CollapseButtonStyled onClick={() => setCollapsed(!collapsed)}>
<IconColumnStyled>
{collapsed ? (
<>
<IconUp size="tiny" />
<IconDown size="tiny" />
</>
) : (
<>
<IconDown size="tiny" />
<IconUp size="tiny" />
</>
)}
</IconColumnStyled>
<LabelStyled theme={theme}>
{collapsed ? 'Show ' : 'Hide '}
{props.label}
</LabelStyled>
</CollapseButtonStyled>
{!collapsed && props.type !== 'image' && (
<CopyButtonStyled
onClick={() => copyCode(content)}
icon={<IconCopy />}
size="small"
label="Copy"
display="icon"
/>
)}
</LineStyled>
{!collapsed && content ? <ContentWrapperStyled>{content}</ContentWrapperStyled> : <></>}
<WrapperStyled theme={theme} usePre={props.usePre} wide={props.wide}>
<ConditionalWrapper
condition={props.usePre}
wrapper={(children) => <pre>{children}</pre>}
>
<LineStyled>
<div className="btn-link">
<CollapseButtonStyled onClick={() => setCollapsed(!collapsed)}>
<CollapseButtonContentStyled theme={theme}>
<IconColumnStyled>
{collapsed ? (
<>
<IconUp size="tiny" />
<IconDown size="tiny" />
</>
) : (
<>
<IconDown size="tiny" />
<IconUp size="tiny" />
</>
)}
</IconColumnStyled>
{!props.hideState && (collapsed ? 'Show ' : 'Hide ')}
</CollapseButtonContentStyled>
</CollapseButtonStyled>
</div>
<HeaderWraperStyled>{props.header}</HeaderWraperStyled>
{!collapsed && props.copyable && (
<CopyButtonStyled
onClick={() => copyCode(content)}
icon={<IconCopy />}
size="small"
label="Copy"
display="icon"
/>
)}
</LineStyled>
{!collapsed && content ? <ContentWrapperStyled>{content}</ContentWrapperStyled> : <></>}
</ConditionalWrapper>
</WrapperStyled>
)}
</>
Expand Down

0 comments on commit bb44e1d

Please sign in to comment.