feat: add Select component and use for configurator
This commit is contained in:
@@ -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',
|
||||
};
|
||||
|
||||
168
packages/website/ts/components/ui/select.tsx
Normal file
168
packages/website/ts/components/ui/select.tsx
Normal 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>
|
||||
);
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
38
packages/website/ts/pages/instant/config_generator.tsx
Normal file
38
packages/website/ts/pages/instant/config_generator.tsx
Normal 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);
|
||||
};
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user