Styled configurator
This commit is contained in:
@@ -10,7 +10,9 @@ interface BaseTextInterface extends PaddingInterface {
|
||||
interface HeadingProps extends BaseTextInterface {
|
||||
asElement?: 'h1'| 'h2'| 'h3'| 'h4';
|
||||
maxWidth?: string;
|
||||
fontWeight?: string;
|
||||
isCentered?: boolean;
|
||||
isFlex?: boolean;
|
||||
isNoMargin?: boolean;
|
||||
isMuted?: boolean | number;
|
||||
marginBottom?: string;
|
||||
@@ -26,6 +28,9 @@ interface ParagraphProps extends BaseTextInterface {
|
||||
const StyledHeading = styled.h1<HeadingProps>`
|
||||
max-width: ${props => props.maxWidth};
|
||||
color: ${props => props.color || props.theme.textColor};
|
||||
display: ${props => props.isFlex && `inline-flex`};
|
||||
align-items: center;
|
||||
justify-content: ${props => props.isFlex && `space-between`};
|
||||
font-size: ${props => isNaN(props.size) ? `var(--${props.size || 'default'}Heading)` : `${props.size}px`};
|
||||
line-height: ${props => `var(--${props.size || 'default'}HeadingHeight)`};
|
||||
text-align: ${props => props.isCentered && 'center'};
|
||||
@@ -34,7 +39,8 @@ const StyledHeading = styled.h1<HeadingProps>`
|
||||
margin-right: ${props => props.isCentered && 'auto'};
|
||||
margin-bottom: ${props => !props.isNoMargin && (props.marginBottom || '30px')};
|
||||
opacity: ${props => typeof props.isMuted === 'boolean' ? 0.75 : props.isMuted};
|
||||
font-weight: ${props => ['h4'].includes(props.asElement) ? 400 : 300};
|
||||
font-weight: ${props => props.fontWeight ? props.fontWeight : (['h4'].includes(props.asElement) ? 400 : 300)};
|
||||
width: ${props => props.isFlex && `100%`};
|
||||
`;
|
||||
|
||||
export const Heading: React.StatelessComponent<HeadingProps> = props => {
|
||||
|
||||
179
packages/website/ts/@next/pages/instant/code_demo.tsx
Normal file
179
packages/website/ts/@next/pages/instant/code_demo.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
import * as React from 'react';
|
||||
import * as CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
|
||||
import { Button } from 'ts/components/ui/button';
|
||||
import { Container } from 'ts/components/ui/container';
|
||||
import { colors } from 'ts/style/colors';
|
||||
import { styled } from 'ts/style/theme';
|
||||
import { zIndex } from 'ts/style/z_index';
|
||||
|
||||
const CustomPre = styled.pre`
|
||||
margin: 0px;
|
||||
line-height: 24px;
|
||||
overflow: scroll;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 800px;
|
||||
border-radius: 4px;
|
||||
code {
|
||||
background-color: inherit !important;
|
||||
border-radius: 0px;
|
||||
font-family: 'Roboto Mono', sans-serif;
|
||||
border: none;
|
||||
}
|
||||
code:first-of-type {
|
||||
background-color: #060D0D !important;
|
||||
color: #999;
|
||||
min-height: 100%;
|
||||
text-align: center;
|
||||
margin-right: 15px;
|
||||
line-height: 25px;
|
||||
padding: 10px 7px !important;
|
||||
}
|
||||
code:last-of-type {
|
||||
position: relative;
|
||||
top: 10px;
|
||||
top: 0;
|
||||
padding-top: 11px;
|
||||
display: inline-block;
|
||||
line-height: 25px;
|
||||
}
|
||||
`;
|
||||
|
||||
const customStyle = {
|
||||
'hljs-comment': {
|
||||
color: '#7e7887',
|
||||
},
|
||||
'hljs-quote': {
|
||||
color: '#7e7887',
|
||||
},
|
||||
'hljs-variable': {
|
||||
color: '#be4678',
|
||||
},
|
||||
'hljs-template-variable': {
|
||||
color: '#be4678',
|
||||
},
|
||||
'hljs-attribute': {
|
||||
color: '#be4678',
|
||||
},
|
||||
'hljs-regexp': {
|
||||
color: '#be4678',
|
||||
},
|
||||
'hljs-link': {
|
||||
color: '#be4678',
|
||||
},
|
||||
'hljs-tag': {
|
||||
color: '#61f5ff',
|
||||
},
|
||||
'hljs-name': {
|
||||
color: '#61f5ff',
|
||||
},
|
||||
'hljs-selector-id': {
|
||||
color: '#be4678',
|
||||
},
|
||||
'hljs-selector-class': {
|
||||
color: '#be4678',
|
||||
},
|
||||
'hljs-number': {
|
||||
color: '#c994ff',
|
||||
},
|
||||
'hljs-meta': {
|
||||
color: '#61f5ff',
|
||||
},
|
||||
'hljs-built_in': {
|
||||
color: '#aa573c',
|
||||
},
|
||||
'hljs-builtin-name': {
|
||||
color: '#aa573c',
|
||||
},
|
||||
'hljs-literal': {
|
||||
color: '#aa573c',
|
||||
},
|
||||
'hljs-type': {
|
||||
color: '#aa573c',
|
||||
},
|
||||
'hljs-params': {
|
||||
color: '#aa573c',
|
||||
},
|
||||
'hljs-string': {
|
||||
color: '#bcff88',
|
||||
},
|
||||
'hljs-symbol': {
|
||||
color: '#2a9292',
|
||||
},
|
||||
'hljs-bullet': {
|
||||
color: '#2a9292',
|
||||
},
|
||||
'hljs-title': {
|
||||
color: '#576ddb',
|
||||
},
|
||||
'hljs-section': {
|
||||
color: '#576ddb',
|
||||
},
|
||||
'hljs-keyword': {
|
||||
color: '#955ae7',
|
||||
},
|
||||
'hljs-selector-tag': {
|
||||
color: '#955ae7',
|
||||
},
|
||||
'hljs-deletion': {
|
||||
color: '#19171c',
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
backgroundColor: '#be4678',
|
||||
},
|
||||
'hljs-addition': {
|
||||
color: '#19171c',
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
backgroundColor: '#2a9292',
|
||||
},
|
||||
hljs: {
|
||||
display: 'block',
|
||||
overflowX: 'hidden',
|
||||
background: '#1B2625',
|
||||
color: 'white',
|
||||
fontSize: '12px',
|
||||
},
|
||||
'hljs-emphasis': {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
'hljs-strong': {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
};
|
||||
|
||||
export interface CodeDemoProps {
|
||||
children: string;
|
||||
}
|
||||
|
||||
export interface CodeDemoState {
|
||||
didCopyCode: boolean;
|
||||
}
|
||||
|
||||
export class CodeDemo extends React.Component<CodeDemoProps, CodeDemoState> {
|
||||
public state: CodeDemoState = {
|
||||
didCopyCode: false,
|
||||
};
|
||||
public render(): React.ReactNode {
|
||||
const copyButtonText = this.state.didCopyCode ? 'Copied!' : 'Copy';
|
||||
return (
|
||||
<Container position="relative" height="100%">
|
||||
<Container position="absolute" top="10px" right="10px" zIndex={zIndex.overlay - 1}>
|
||||
<CopyToClipboard text={this.props.children} onCopy={this._handleCopyClick}>
|
||||
<Button fontSize="14px">
|
||||
<b>{copyButtonText}</b>
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
</Container>
|
||||
<SyntaxHighlighter language="html" style={customStyle} showLineNumbers={true} PreTag={CustomPre}>
|
||||
{this.props.children}
|
||||
</SyntaxHighlighter>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
private readonly _handleCopyClick = () => {
|
||||
this.setState({ didCopyCode: true });
|
||||
};
|
||||
}
|
||||
@@ -4,14 +4,14 @@ import { assetDataUtils } from '@0x/order-utils';
|
||||
import { ObjectMap } from '@0x/types';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { CheckMark } from 'ts/components/ui/check_mark';
|
||||
import { Container } from 'ts/components/ui/container';
|
||||
import { MultiSelect } from 'ts/components/ui/multi_select';
|
||||
import { Select, SelectItemConfig } from 'ts/components/ui/select';
|
||||
import { Spinner } from 'ts/components/ui/spinner';
|
||||
import { Text } from 'ts/components/ui/text';
|
||||
import { ConfigGeneratorAddressInput } from 'ts/pages/instant/config_generator_address_input';
|
||||
import { ConfigGeneratorAddressInput } from 'ts/@next/pages/instant/config_generator_address_input';
|
||||
import { FeePercentageSlider } from 'ts/pages/instant/fee_percentage_slider';
|
||||
import { colors } from 'ts/style/colors';
|
||||
import { WebsitePaths } from 'ts/types';
|
||||
@@ -19,6 +19,7 @@ import { constants } from 'ts/utils/constants';
|
||||
|
||||
// New components
|
||||
import { Heading, Paragraph } from 'ts/@next/components/text';
|
||||
import { Select, SelectItemConfig } from 'ts/@next/pages/instant/select';
|
||||
|
||||
import { assetMetaDataMap } from '../../../../../instant/src/data/asset_meta_data_map';
|
||||
import { ERC20AssetMetaData, ZeroExInstantBaseConfig } from '../../../../../instant/src/types';
|
||||
@@ -62,8 +63,8 @@ export class ConfigGenerator extends React.Component<ConfigGeneratorProps, Confi
|
||||
}
|
||||
return (
|
||||
<Container minWidth="350px">
|
||||
<ConfigGeneratorSection title="Standard relayer API endpoint">
|
||||
<Select value={value.orderSource} items={this._generateItems()} />
|
||||
<ConfigGeneratorSection title="Liquidity Source">
|
||||
<Select id="" value={value.orderSource} items={this._generateItems()} />
|
||||
</ConfigGeneratorSection>
|
||||
<ConfigGeneratorSection {...this._getTokenSelectorProps()}>
|
||||
{this._renderTokenMultiSelectOrSpinner()}
|
||||
@@ -113,7 +114,8 @@ export class ConfigGenerator extends React.Component<ConfigGeneratorProps, Confi
|
||||
};
|
||||
private readonly _generateItems = (): SelectItemConfig[] => {
|
||||
return _.map(SRA_ENDPOINTS, endpoint => ({
|
||||
text: endpoint,
|
||||
label: endpoint,
|
||||
value: endpoint,
|
||||
onClick: this._handleSRASelection.bind(this, endpoint),
|
||||
}));
|
||||
};
|
||||
@@ -252,15 +254,9 @@ export class ConfigGenerator extends React.Component<ConfigGeneratorProps, Confi
|
||||
renderItemContent: (isSelected: boolean) => (
|
||||
<Container className="flex items-center">
|
||||
<Container marginRight="10px">
|
||||
<CheckMark isChecked={isSelected} />
|
||||
<CheckMark isChecked={isSelected} color={colors.brandLight} />
|
||||
</Container>
|
||||
<Text
|
||||
fontSize="16px"
|
||||
fontColor={isSelected ? colors.mediumBlue : colors.darkerGrey}
|
||||
fontWeight={300}
|
||||
>
|
||||
<b>{metaData.symbol.toUpperCase()}</b> — {metaData.name}
|
||||
</Text>
|
||||
<CheckboxText isSelected={isSelected}>{metaData.symbol.toUpperCase()} — {metaData.name}</CheckboxText>
|
||||
</Container>
|
||||
),
|
||||
onClick: this._handleTokenClick.bind(this, assetData),
|
||||
@@ -288,27 +284,57 @@ export const ConfigGeneratorSection: React.StatelessComponent<ConfigGeneratorSec
|
||||
}) => (
|
||||
<Container marginBottom={marginBottom}>
|
||||
<Container marginBottom="10px" className="flex justify-between items-center">
|
||||
<Container>
|
||||
<Heading size="small" isNoMargin={true}>
|
||||
{title}
|
||||
</Heading>
|
||||
<Heading size="small" marginBottom="0" isFlex={true}>
|
||||
<span>{title}</span>
|
||||
{isOptional && (
|
||||
<Text fontColor={colors.grey} fontSize="16px" lineHeight="18px" display="inline">
|
||||
{' '}
|
||||
(optional)
|
||||
</Text>
|
||||
<OptionalText>
|
||||
{' '}
|
||||
Optional
|
||||
</OptionalText>
|
||||
)}
|
||||
</Container>
|
||||
</Heading>
|
||||
{actionText && (
|
||||
<Text fontSize="12px" fontColor={colors.grey} onClick={onActionTextClick}>
|
||||
<OptionalAction onClick={onActionTextClick}>
|
||||
{actionText}
|
||||
</Text>
|
||||
</OptionalAction>
|
||||
)}
|
||||
</Container>
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
|
||||
const Mark = ({ checked }) => (
|
||||
<StyledMark checked={checked}>
|
||||
{checked && ''}
|
||||
</StyledMark>
|
||||
);
|
||||
|
||||
const StyledMark = styled.div`
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-color: ${props => props.checked && colors.brandLight};
|
||||
background-color: ${props => props.checked && colors.brandLight};
|
||||
`;
|
||||
|
||||
ConfigGeneratorSection.defaultProps = {
|
||||
marginBottom: '30px',
|
||||
};
|
||||
|
||||
const OptionalText = styled.span`
|
||||
display: inline;
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
const CheckboxText = styled.span`
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
color: ${props => props.isSelected ? colors.brandDark : '#666666'}
|
||||
`;
|
||||
|
||||
const OptionalAction = styled(OptionalText)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
@@ -0,0 +1,90 @@
|
||||
import { addressUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { colors } from 'ts/style/colors';
|
||||
|
||||
import { Container } from 'ts/components/ui/container';
|
||||
import { Text } from 'ts/components/ui/text';
|
||||
import { Paragraph } from 'ts/@next/components/text';
|
||||
|
||||
export interface ConfigGeneratorAddressInputProps {
|
||||
value?: string;
|
||||
onChange?: (address: string, isValid: boolean) => void;
|
||||
}
|
||||
|
||||
export interface ConfigGeneratorAddressInputState {
|
||||
errMsg: string;
|
||||
}
|
||||
|
||||
export interface InputProps {
|
||||
className?: string;
|
||||
value?: string;
|
||||
width?: string;
|
||||
fontSize?: string;
|
||||
fontColor?: string;
|
||||
border?: string;
|
||||
padding?: string;
|
||||
placeholderColor?: string;
|
||||
placeholder?: string;
|
||||
backgroundColor?: string;
|
||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
export class ConfigGeneratorAddressInput extends React.Component<
|
||||
ConfigGeneratorAddressInputProps,
|
||||
ConfigGeneratorAddressInputState
|
||||
> {
|
||||
public state = {
|
||||
errMsg: '',
|
||||
};
|
||||
public render(): React.ReactNode {
|
||||
const { errMsg } = this.state;
|
||||
const hasError = !_.isEmpty(errMsg);
|
||||
const border = hasError ? '1px solid red' : undefined;
|
||||
return (
|
||||
<Container height="80px">
|
||||
<Input
|
||||
value={this.props.value}
|
||||
onChange={this._handleChange}
|
||||
placeholder="0xe99...aa8da4"
|
||||
/>
|
||||
<Container marginTop="5px" isHidden={!hasError} height="25px">
|
||||
<Paragraph size="small" isNoMargin={true}>
|
||||
{errMsg}
|
||||
</Paragraph>
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const address = event.target.value;
|
||||
const isValidAddress = addressUtils.isAddress(address.toLowerCase()) || address === '';
|
||||
const errMsg = isValidAddress ? '' : 'Please enter a valid Ethereum address';
|
||||
this.setState({
|
||||
errMsg,
|
||||
});
|
||||
this.props.onChange(address, isValidAddress);
|
||||
};
|
||||
}
|
||||
|
||||
const PlainInput: React.StatelessComponent<InputProps> = ({ value, className, placeholder, onChange }) => (
|
||||
<input className={className} value={value} onChange={onChange} placeholder={placeholder} />
|
||||
);
|
||||
|
||||
export const Input = styled(PlainInput)`
|
||||
background-color: ${colors.white};
|
||||
color: ${colors.textDarkSecondary};
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
padding: 16px 20px 18px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
outline: none;
|
||||
&::placeholder {
|
||||
color: #333333;
|
||||
opacity: 0.5;
|
||||
}
|
||||
`;
|
||||
@@ -4,8 +4,8 @@ import styled from 'styled-components';
|
||||
|
||||
import { colors } from 'ts/style/colors';
|
||||
|
||||
import { CodeDemo } from 'ts/@next/pages/instant/code_demo';
|
||||
import { ConfigGenerator } from 'ts/@next/pages/instant/config_generator';
|
||||
import { CodeDemo } from 'ts/pages/instant/code_demo';
|
||||
import { Column, FlexWrap, Section } from 'ts/@next/components/newLayout';
|
||||
import { Heading, Paragraph } from 'ts/@next/components/text';
|
||||
import { WebsitePaths } from 'ts/types';
|
||||
@@ -44,7 +44,7 @@ export class Configurator extends React.Component<ConfiguratorProps> {
|
||||
</Column>
|
||||
<Column width="560px">
|
||||
<HeadingWrapper>
|
||||
<Heading size="small">Code Snippet</Heading>
|
||||
<Heading size="small" marginBottom="15px">Code Snippet</Heading>
|
||||
<Link
|
||||
href={`${WebsitePaths.Wiki}#Get-Started-With-Instant`}
|
||||
isBlock={true}
|
||||
@@ -87,10 +87,10 @@ export class Configurator extends React.Component<ConfiguratorProps> {
|
||||
)}`
|
||||
: ''
|
||||
}
|
||||
}, 'body');
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
}, 'body');
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
};
|
||||
private readonly _renderAvailableAssetDatasString = (availableAssetDatas: string[]): string => {
|
||||
const stringAvailableAssetDatas = availableAssetDatas.map(assetData => `'${assetData}'`);
|
||||
|
||||
66
packages/website/ts/@next/pages/instant/select.tsx
Normal file
66
packages/website/ts/@next/pages/instant/select.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { colors } from 'ts/style/colors';
|
||||
|
||||
import {Column, Section, Wrap, WrapCentered} from 'ts/@next/components/layout';
|
||||
import {Heading, Paragraph} from 'ts/@next/components/text';
|
||||
|
||||
export interface SelectItemConfig {
|
||||
label: string;
|
||||
value?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
interface SelectProps {
|
||||
value?: string;
|
||||
id: string;
|
||||
items: SelectItemConfig[];
|
||||
emptyText?: string;
|
||||
}
|
||||
|
||||
export const Select: React.FunctionComponent<SelectProps> = ({ value, id, items, emptyText }) => {
|
||||
return (
|
||||
<Container>
|
||||
<StyledSelect id={id}>
|
||||
<option value="">{emptyText}</option>
|
||||
{items.map((item, index) => <option key={`${id}-item-${index}`} value={item.value} selected={item.value === value} onClick={item.onClick}>{item.label}</option>)}
|
||||
</StyledSelect>
|
||||
<Caret width="12" height="7" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 1L6 6 1 1" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></Caret>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
Select.defaultProps = {
|
||||
emptyText: 'Select...',
|
||||
};
|
||||
|
||||
const Container = styled.div`
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const StyledSelect = styled.select`
|
||||
appearance: none;
|
||||
border: 0;
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
padding: 20px 20px 20px 20px;
|
||||
`;
|
||||
|
||||
const SelectAllButton = styled.button`
|
||||
appearance: none;
|
||||
border: 0;
|
||||
font-size: 0.777777778rem;
|
||||
display: block;
|
||||
opacity: 0.75;
|
||||
`;
|
||||
|
||||
const Caret = styled.svg`
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: calc(50% - 4px);
|
||||
`;
|
||||
Reference in New Issue
Block a user