feat: implement basic dropdown component

This commit is contained in:
fragosti
2018-11-07 20:30:45 -08:00
parent 95b2898b9c
commit c0d8ceca82
6 changed files with 169 additions and 8 deletions

View File

@@ -113,7 +113,7 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput
}
return (
<Container marginLeft="5px">
<Icon icon="chevron" width={12} onClick={this._handleSelectAssetClick} />
<Icon icon="chevron" width={12} stroke={ColorOption.white} onClick={this._handleSelectAssetClick} />
</Container>
);
};

View File

@@ -0,0 +1,31 @@
import * as _ from 'lodash';
import * as React from 'react';
import { ColorOption } from '../style/theme';
import { Container } from './ui/container';
import { Dropdown } from './ui/dropdown';
import { Text } from './ui/text';
export interface PaymentMethodProps {}
export class PaymentMethod extends React.Component<PaymentMethodProps> {
public render(): React.ReactNode {
return (
<Container padding="20px" width="100%">
<Container marginBottom="10px">
<Text
letterSpacing="1px"
fontColor={ColorOption.primaryColor}
fontWeight={600}
textTransform="uppercase"
fontSize="14px"
>
Payment Method
</Text>
</Container>
<Dropdown value="0x00000000000000" label="25.33 ETH" />
</Container>
);
}
}

View File

@@ -0,0 +1,127 @@
import * as _ from 'lodash';
import * as React from 'react';
import { ColorOption } from '../../style/theme';
import { zIndex } from '../../style/z_index';
import { Container } from './container';
import { Flex } from './flex';
import { Icon } from './icon';
import { Text } from './text';
export interface DropdownItemConfig {
text: string;
onClick?: () => void;
}
export interface DropdownProps {
value: string;
label: string;
items: DropdownItemConfig[];
}
export interface DropdownState {
isOpen: boolean;
}
export class Dropdown extends React.Component<DropdownProps, DropdownState> {
public static defaultProps = {
items: [
{
text: 'Item 1',
},
{
text: 'Item 2',
},
],
};
public state: DropdownState = {
isOpen: false,
};
public render(): React.ReactNode {
const { value, label, items } = this.props;
const { isOpen } = this.state;
const hasItems = !_.isEmpty(items);
const borderRadius = isOpen ? '4px 4px 0px 0px' : '4px';
return (
<Container position="relative">
<Container
cursor={hasItems ? 'pointer' : undefined}
onClick={this._handleDropdownClick}
hasBoxShadow={true}
borderRadius={borderRadius}
border="1px solid"
borderColor={ColorOption.feintGrey}
padding="0.8em"
borderBottom="1px solid"
>
<Flex justify="space-between">
<Text fontSize="16px" lineHeight="19px" fontColor={ColorOption.darkGrey}>
{value}
</Text>
<Container>
<Text fontSize="16px" lineHeight="17px" fontColor={ColorOption.lightGrey}>
{label}
</Text>
{hasItems && (
<Container marginLeft="5px" display="inline-block" position="relative" bottom="2px">
<Icon padding="3px" icon="chevron" width={12} stroke={ColorOption.grey} />
</Container>
)}
</Container>
</Flex>
</Container>
{isOpen && (
<Container
width="100%"
position="absolute"
onClick={this._closeDropdown}
backgroundColor={ColorOption.white}
hasBoxShadow={true}
zIndex={zIndex.dropdownItems}
>
{_.map(items, (item, index) => (
<DropdownItem key={item.text} {...item} isLast={index === items.length - 1} />
))}
</Container>
)}
</Container>
);
}
private readonly _handleDropdownClick = (): void => {
if (_.isEmpty(this.props.items)) {
return;
}
this.setState({
isOpen: !this.state.isOpen,
});
};
private readonly _closeDropdown = (): void => {
this.setState({
isOpen: false,
});
};
}
export interface DropdownItemProps extends DropdownItemConfig {
text: string;
onClick?: () => void;
isLast: boolean;
}
export const DropdownItem: React.StatelessComponent<DropdownItemProps> = ({ text, onClick, isLast }) => (
<Container
onClick={onClick}
cursor="pointer"
darkenOnHover={true}
backgroundColor={ColorOption.white}
padding="0.8em"
borderTop="0"
border="1px solid"
borderRadius={isLast ? '0px 0px 4px 4px' : undefined}
width="100%"
borderColor={ColorOption.feintGrey}
>
<Text>{text}</Text>
</Container>
);

View File

@@ -9,7 +9,6 @@ interface IconInfo {
path: string;
fillRule?: svgRule;
clipRule?: svgRule;
stroke?: string;
strokeOpacity?: number;
strokeWidth?: number;
strokeLinecap?: 'butt' | 'round' | 'square' | 'inherit';
@@ -47,7 +46,6 @@ const ICONS: IconInfoMapping = {
chevron: {
viewBox: '0 0 12 7',
path: 'M11 1L6 6L1 1',
stroke: 'white',
strokeOpacity: 0.5,
strokeWidth: 1.5,
strokeLinecap: 'round',
@@ -67,6 +65,7 @@ export interface IconProps {
width: number;
height?: number;
color?: ColorOption;
stroke?: ColorOption;
icon: keyof IconInfoMapping;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
padding?: string;
@@ -75,6 +74,7 @@ export interface IconProps {
const PlainIcon: React.StatelessComponent<IconProps> = props => {
const iconInfo = ICONS[props.icon];
const colorValue = _.isUndefined(props.color) ? undefined : props.theme[props.color];
const strokeValue = _.isUndefined(props.stroke) ? undefined : props.theme[props.stroke];
return (
<div onClick={props.onClick} className={props.className}>
<svg
@@ -89,7 +89,7 @@ const PlainIcon: React.StatelessComponent<IconProps> = props => {
fill={colorValue}
fillRule={iconInfo.fillRule || 'nonzero'}
clipRule={iconInfo.clipRule || 'nonzero'}
stroke={iconInfo.stroke}
stroke={strokeValue}
strokeOpacity={iconInfo.strokeOpacity}
strokeWidth={iconInfo.strokeWidth}
strokeLinecap={iconInfo.strokeLinecap}
@@ -101,7 +101,8 @@ const PlainIcon: React.StatelessComponent<IconProps> = props => {
};
export const Icon = withTheme(styled(PlainIcon)`
cursor: ${props => (!_.isUndefined(props.onClick) ? 'pointer' : 'default')};
display: inline-block;
${props => (!_.isUndefined(props.onClick) ? 'cursor: pointer' : '')};
transition: opacity 0.5s ease;
padding: ${props => props.padding};
opacity: ${props => (!_.isUndefined(props.onClick) ? 0.7 : 1)};

View File

@@ -3,15 +3,15 @@ import * as React from 'react';
import { AvailableERC20TokenSelector } from '../containers/available_erc20_token_selector';
import { LatestBuyQuoteOrderDetails } from '../containers/latest_buy_quote_order_details';
import { LatestError } from '../containers/latest_error';
import { SelectedAssetBuyOrderProgress } from '../containers/selected_asset_buy_order_progress';
import { SelectedAssetBuyOrderStateButtons } from '../containers/selected_asset_buy_order_state_buttons';
import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading';
import { SelectedAssetBuyOrderProgress } from '../containers/selected_asset_buy_order_progress';
import { ColorOption } from '../style/theme';
import { zIndex } from '../style/z_index';
import { SlideAnimationState } from './animations/slide_animation';
import { PaymentMethod } from './payment_method';
import { SlidingPanel } from './sliding_panel';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
@@ -41,6 +41,7 @@ export class ZeroExInstantContainer extends React.Component<ZeroExInstantContain
>
<Flex direction="column" justify="flex-start">
<SelectedAssetInstantHeading onSelectAssetClick={this._handleSymbolClick} />
<PaymentMethod />
<SelectedAssetBuyOrderProgress />
<LatestBuyQuoteOrderDetails />
<Container padding="20px" width="100%">

View File

@@ -1,5 +1,6 @@
export const zIndex = {
errorPopup: 1,
mainContainer: 2,
panel: 3,
dropdownItems: 3,
panel: 4,
};