From 1c413e632b3a71453a523d68507e0f464f0f61bc Mon Sep 17 00:00:00 2001 From: Fred Carlsen Date: Thu, 13 Dec 2018 17:38:18 +0100 Subject: Styled configurator --- packages/website/ts/@next/components/text.tsx | 8 +- .../website/ts/@next/pages/instant/code_demo.tsx | 179 +++++++++++++++++++++ .../ts/@next/pages/instant/config_generator.tsx | 74 ++++++--- .../instant/config_generator_address_input.tsx | 90 +++++++++++ .../ts/@next/pages/instant/configurator.tsx | 12 +- packages/website/ts/@next/pages/instant/select.tsx | 66 ++++++++ 6 files changed, 398 insertions(+), 31 deletions(-) create mode 100644 packages/website/ts/@next/pages/instant/code_demo.tsx create mode 100644 packages/website/ts/@next/pages/instant/config_generator_address_input.tsx create mode 100644 packages/website/ts/@next/pages/instant/select.tsx (limited to 'packages') 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` 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` 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 = 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 { + public state: CodeDemoState = { + didCopyCode: false, + }; + public render(): React.ReactNode { + const copyButtonText = this.state.didCopyCode ? 'Copied!' : 'Copy'; + return ( + + + + + + + + {this.props.children} + + + ); + } + 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 - - {this._renderTokenMultiSelectOrSpinner()} @@ -113,7 +114,8 @@ export class ConfigGenerator extends React.Component { 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 ( - + - - {metaData.symbol.toUpperCase()} — {metaData.name} - + {metaData.symbol.toUpperCase()} — {metaData.name} ), onClick: this._handleTokenClick.bind(this, assetData), @@ -288,27 +284,57 @@ export const ConfigGeneratorSection: React.StatelessComponent ( - - - {title} - + + {title} {isOptional && ( - - {' '} - (optional) - + + {' '} + Optional + )} - + {actionText && ( - + {actionText} - + )} {children} ); +const Mark = ({ checked }) => ( + + {checked && ''} + +); + +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) => 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 ( + + + + + {errMsg} + + + + ); + } + + private readonly _handleChange = (event: React.ChangeEvent): 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 = ({ value, className, placeholder, onChange }) => ( + +); + +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 { - Code Snippet + Code Snippet { )}` : '' } - }, 'body'); - - -`; + }, 'body'); + + + `; }; 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 = ({ value, id, items, emptyText }) => { + return ( + + + + {items.map((item, index) => )} + + + + ); +}; + +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); +`; -- cgit v1.2.3