diff options
author | Fred Carlsen <fred@sjelfull.no> | 2018-12-14 00:38:18 +0800 |
---|---|---|
committer | Fred Carlsen <fred@sjelfull.no> | 2018-12-14 00:38:32 +0800 |
commit | 1c413e632b3a71453a523d68507e0f464f0f61bc (patch) | |
tree | ee1945846765ad6d4e012b9e5661d5f5d26d68bd | |
parent | 803086da5769c7cad2b4bbb80496ea58b353e5b8 (diff) | |
download | dexon-0x-contracts-1c413e632b3a71453a523d68507e0f464f0f61bc.tar.gz dexon-0x-contracts-1c413e632b3a71453a523d68507e0f464f0f61bc.tar.zst dexon-0x-contracts-1c413e632b3a71453a523d68507e0f464f0f61bc.zip |
Styled configurator
6 files changed, 398 insertions, 31 deletions
diff --git a/packages/website/ts/@next/components/text.tsx b/packages/website/ts/@next/components/text.tsx index 54d4764f3..c29277c16 100644 --- a/packages/website/ts/@next/components/text.tsx +++ b/packages/website/ts/@next/components/text.tsx @@ -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 => { diff --git a/packages/website/ts/@next/pages/instant/code_demo.tsx b/packages/website/ts/@next/pages/instant/code_demo.tsx new file mode 100644 index 000000000..fe11175d5 --- /dev/null +++ b/packages/website/ts/@next/pages/instant/code_demo.tsx @@ -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 }); + }; +} diff --git a/packages/website/ts/@next/pages/instant/config_generator.tsx b/packages/website/ts/@next/pages/instant/config_generator.tsx index 00e491431..119311d94 100644 --- a/packages/website/ts/@next/pages/instant/config_generator.tsx +++ b/packages/website/ts/@next/pages/instant/config_generator.tsx @@ -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; +`;
\ No newline at end of file diff --git a/packages/website/ts/@next/pages/instant/config_generator_address_input.tsx b/packages/website/ts/@next/pages/instant/config_generator_address_input.tsx new file mode 100644 index 000000000..01453d445 --- /dev/null +++ b/packages/website/ts/@next/pages/instant/config_generator_address_input.tsx @@ -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; + } +`;
\ No newline at end of file diff --git a/packages/website/ts/@next/pages/instant/configurator.tsx b/packages/website/ts/@next/pages/instant/configurator.tsx index 354a7c205..cdba53ca9 100644 --- a/packages/website/ts/@next/pages/instant/configurator.tsx +++ b/packages/website/ts/@next/pages/instant/configurator.tsx @@ -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}'`); diff --git a/packages/website/ts/@next/pages/instant/select.tsx b/packages/website/ts/@next/pages/instant/select.tsx new file mode 100644 index 000000000..7c601da1c --- /dev/null +++ b/packages/website/ts/@next/pages/instant/select.tsx @@ -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); +`; |