feat: add Select component and use for configurator

This commit is contained in:
fragosti
2018-11-29 15:57:35 -08:00
parent 0af07bcf49
commit f80768cae0
7 changed files with 285 additions and 31 deletions

View File

@@ -1,6 +1,9 @@
import { TextAlignProperty } from 'csstype';
import { darken } from 'polished';
import * as React from 'react';
import { styled } from 'ts/style/theme';
type StringOrNum = string | number;
export type ContainerTag = 'div' | 'span';
@@ -17,10 +20,13 @@ export interface ContainerProps {
paddingLeft?: StringOrNum;
backgroundColor?: string;
background?: string;
border?: string;
borderTop?: string;
borderRadius?: StringOrNum;
borderBottomLeftRadius?: StringOrNum;
borderBottomRightRadius?: StringOrNum;
borderBottom?: StringOrNum;
borderColor?: string;
maxWidth?: StringOrNum;
maxHeight?: StringOrNum;
width?: StringOrNum;
@@ -42,10 +48,23 @@ export interface ContainerProps {
id?: string;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
overflowX?: 'scroll' | 'hidden' | 'auto' | 'visible';
shouldDarkenOnHover?: boolean;
shouldAddBoxShadowOnHover?: boolean;
}
export const Container: React.StatelessComponent<ContainerProps> = props => {
const { children, className, Tag, isHidden, id, onClick, ...style } = props;
export const PlainContainer: React.StatelessComponent<ContainerProps> = props => {
const {
children,
className,
Tag,
isHidden,
id,
onClick,
shouldDarkenOnHover,
shouldAddBoxShadowOnHover,
// tslint:disable-next-line:trailing-comma
...style
} = props;
const visibility = isHidden ? 'hidden' : undefined;
return (
<Tag id={id} style={{ ...style, visibility }} className={className} onClick={onClick}>
@@ -54,6 +73,16 @@ export const Container: React.StatelessComponent<ContainerProps> = props => {
);
};
export const Container = styled(PlainContainer)`
&:hover {
${props =>
props.shouldDarkenOnHover
? `background-color: ${props.backgroundColor ? darken(0.05, props.backgroundColor) : 'none'} !important`
: ''};
${props => (props.shouldAddBoxShadowOnHover ? 'box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)' : '')};
}
`;
Container.defaultProps = {
Tag: 'div',
};

View File

@@ -0,0 +1,168 @@
import { colors } from '@0x/react-shared';
import * as _ from 'lodash';
import * as React from 'react';
import { zIndex } from 'ts/style/z_index';
import { Container } from './container';
import { Overlay } from './overlay';
import { Text } from './text';
export interface SelectItemConfig {
text: string;
onClick?: () => void;
}
export interface SelectProps {
value: string;
label?: string;
items: SelectItemConfig[];
onOpen?: () => void;
border?: string;
fontSize?: string;
iconSize?: number;
textColor?: string;
labelColor?: string;
backgroundColor?: string;
}
export interface SelectState {
isOpen: boolean;
}
export class Select extends React.Component<SelectProps, SelectState> {
public static defaultProps = {
items: [] as SelectItemConfig[],
textColor: colors.black,
backgroundColor: colors.white,
fontSize: '16px',
iconSize: 25,
};
public state: SelectState = {
isOpen: false,
};
public render(): React.ReactNode {
const { value, label, items, border, textColor, labelColor, backgroundColor, fontSize, iconSize } = this.props;
const { isOpen } = this.state;
const hasItems = !_.isEmpty(items);
const borderRadius = isOpen ? '4px 4px 0px 0px' : '4px';
return (
<React.Fragment>
{isOpen && (
<Overlay
style={{
zIndex: zIndex.overlay,
backgroundColor: 'rgba(255, 255, 255, 0)',
}}
onClick={this._closeDropdown}
/>
)}
<Container position="relative">
<Container
cursor={hasItems ? 'pointer' : undefined}
onClick={this._handleDropdownClick}
borderRadius={borderRadius}
border={border}
backgroundColor={backgroundColor}
padding="0.8em"
width="100%"
>
<Container className="flex justify-between">
<Text fontSize={fontSize} fontColor={textColor}>
{value}
</Text>
<Container>
{label && (
<Text fontSize={fontSize} fontColor={labelColor}>
{label}
</Text>
)}
{hasItems && (
<Container marginLeft="5px" display="inline-block" position="relative" bottom="2px">
<i
className="zmdi zmdi-chevron-down"
style={{ fontSize: iconSize, color: colors.darkGrey }}
/>
</Container>
)}
</Container>
</Container>
</Container>
{isOpen && (
<Container
width="100%"
position="absolute"
onClick={this._closeDropdown}
zIndex={zIndex.aboveOverlay}
>
{_.map(items, (item, index) => (
<SelectItem
key={item.text}
{...item}
isLast={index === items.length - 1}
backgroundColor={backgroundColor}
textColor={textColor}
border={border}
/>
))}
</Container>
)}
</Container>
</React.Fragment>
);
}
private readonly _handleDropdownClick = (): void => {
if (_.isEmpty(this.props.items)) {
return;
}
const isOpen = !this.state.isOpen;
this.setState({
isOpen,
});
if (isOpen && this.props.onOpen) {
this.props.onOpen();
}
};
private readonly _closeDropdown = (): void => {
this.setState({
isOpen: false,
});
};
}
export interface SelectItemProps extends SelectItemConfig {
text: string;
onClick?: () => void;
isLast: boolean;
backgroundColor?: string;
border?: string;
textColor?: string;
fontSize?: string;
}
export const SelectItem: React.StatelessComponent<SelectItemProps> = ({
text,
onClick,
isLast,
border,
backgroundColor,
textColor,
fontSize,
}) => (
<Container
onClick={onClick}
cursor="pointer"
backgroundColor={backgroundColor}
padding="0.8em"
borderTop="0"
border={border}
shouldDarkenOnHover={true}
borderRadius={isLast ? '0px 0px 4px 4px' : undefined}
width="100%"
>
<Text fontSize={fontSize} fontColor={textColor}>
{text}
</Text>
</Container>
);

View File

@@ -41,7 +41,7 @@ export class ActionLink extends React.Component<ActionLinkProps> {
);
}
private _handleClick = (event: React.MouseEvent<HTMLElement>) => {
private readonly _handleClick = (event: React.MouseEvent<HTMLElement>) => {
if (!_.isUndefined(this.props.onClick)) {
this.props.onClick();
} else if (!_.isUndefined(this.props.linkSrc)) {

View File

@@ -1,11 +1,8 @@
import * as React from 'react';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { atelierCaveDark } from 'react-syntax-highlighter/styles/hljs';
import { colors } from 'ts/style/colors';
import { styled } from 'ts/style/theme';
import { Container } from 'ts/components/ui/container';
const CustomPre = styled.pre`
margin: 0px;
line-height: 24px;

View File

@@ -0,0 +1,38 @@
import * as _ from 'lodash';
import * as React from 'react';
import { Container } from 'ts/components/ui/container';
import { Select, SelectItemConfig } from 'ts/components/ui/select';
import { ZeroExInstantBaseConfig } from '../../../../instant/src/types';
export interface ConfigGeneratorProps {
value: ZeroExInstantBaseConfig;
onConfigChange: (config: ZeroExInstantBaseConfig) => void;
}
const SRA_ENDPOINTS = ['https://api.radarrelay.com/0x/v2/', 'https://api.openrelay.xyz/v2/'];
export class ConfigGenerator extends React.Component<ConfigGeneratorProps> {
public render(): React.ReactNode {
const { value } = this.props;
return (
<Container>
<Select value={value.orderSource as string} items={this._generateItems()} />
</Container>
);
}
private readonly _generateItems = (): SelectItemConfig[] => {
return _.map(SRA_ENDPOINTS, endpoint => ({
text: endpoint,
onClick: this._handleSRASelection.bind(this, endpoint),
}));
};
private readonly _handleSRASelection = (sraEndpoint: string) => {
const newConfig = {
...this.props.value,
orderSource: sraEndpoint,
};
this.props.onConfigChange(newConfig);
};
}

View File

@@ -4,34 +4,56 @@ import { Container } from 'ts/components/ui/container';
import { Text } from 'ts/components/ui/text';
import { ActionLink } from 'ts/pages/instant/action_link';
import { CodeDemo } from 'ts/pages/instant/code_demo';
import { ConfigGenerator } from 'ts/pages/instant/config_generator';
import { colors } from 'ts/style/colors';
import { ZeroExInstantBaseConfig } from '../../../../instant/src/types';
export interface ConfiguratorProps {
hash: string;
}
export const Configurator = (props: ConfiguratorProps) => (
<Container
className="flex justify-center py4 px3"
id={props.hash}
backgroundColor={colors.instantTertiaryBackground}
>
<Container className="mx3">
<Container className="mb3">
<Text fontSize="20px" lineHeight="28px" fontColor={colors.white} fontWeight={500}>
0x Instant Configurator
</Text>
export interface ConfiguratorState {
instantConfig: ZeroExInstantBaseConfig;
}
export class Configurator extends React.Component<ConfiguratorProps> {
public state = {
instantConfig: {
orderSource: 'https://api.radarrelay.com/0x/v2/',
},
};
public render(): React.ReactNode {
const { hash } = this.props;
return (
<Container
className="flex justify-center py4 px3"
id={hash}
backgroundColor={colors.instantTertiaryBackground}
>
<Container className="mx3">
<Container className="mb3">
<Text fontSize="20px" lineHeight="28px" fontColor={colors.white} fontWeight={500}>
0x Instant Configurator
</Text>
</Container>
<ConfigGenerator value={this.state.instantConfig} onConfigChange={this._handleConfigChange} />
</Container>
<Container className="mx3">
<Container className="mb3 flex justify-between">
<Text fontSize="20px" lineHeight="28px" fontColor={colors.white} fontWeight={500}>
Code Snippet
</Text>
<ActionLink displayText="Explore the Docs" linkSrc="/docs/instant" color={colors.grey} />
</Container>
<CodeDemo />
</Container>
</Container>
<Container height="400px" width="300px" backgroundColor="white" />
</Container>
<Container className="mx3">
<Container className="mb3 flex justify-between">
<Text fontSize="20px" lineHeight="28px" fontColor={colors.white} fontWeight={500}>
Code Snippet
</Text>
<ActionLink displayText="Explore the Docs" linkSrc="/docs/instant" color={colors.grey} />
</Container>
<CodeDemo />
</Container>
</Container>
);
);
}
private readonly _handleConfigChange = (config: ZeroExInstantBaseConfig) => {
this.setState({
instantConfig: config,
});
};
}

View File

@@ -2,9 +2,9 @@ import * as _ from 'lodash';
import * as React from 'react';
import { Container } from 'ts/components/ui/container';
import { ActionLink, ActionLinkProps } from 'ts/pages/instant/action_link';
import { Image } from 'ts/components/ui/image';
import { Text } from 'ts/components/ui/text';
import { ActionLink, ActionLinkProps } from 'ts/pages/instant/action_link';
import { colors } from 'ts/style/colors';
import { ScreenWidths } from 'ts/types';
import { utils } from 'ts/utils/utils';