added announcement banner
This commit is contained in:
195
packages/website/ts/components/annoucement_banner.tsx
Normal file
195
packages/website/ts/components/annoucement_banner.tsx
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { colors } from 'ts/style/colors';
|
||||||
|
|
||||||
|
import { Button } from 'ts/components/button';
|
||||||
|
import { ThemeInterface } from 'ts/components/siteWrap';
|
||||||
|
import { Paragraph } from 'ts/components/text';
|
||||||
|
|
||||||
|
import { Column, Section, SectionProps } from 'ts/components/newLayout';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
heading?: string;
|
||||||
|
subline?: string;
|
||||||
|
onDismiss?: () => void;
|
||||||
|
mainCta?: CTAButton;
|
||||||
|
secondaryCta?: CTAButton;
|
||||||
|
theme?: ThemeInterface;
|
||||||
|
dismissed?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CTAButton {
|
||||||
|
text: string;
|
||||||
|
href?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
shouldOpenInNewTab?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BorderProps {
|
||||||
|
isBottom?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ANNOUNCEMENT_BANNER_HEIGHT = '12rem';
|
||||||
|
|
||||||
|
export const AnnouncementBanner: React.StatelessComponent<Props> = (props: Props) => {
|
||||||
|
const { heading, subline, mainCta, secondaryCta } = props;
|
||||||
|
return (
|
||||||
|
<CustomSection bgColor={colors.brandDark} paddingMobile="120px 0" dismissed={props.dismissed}>
|
||||||
|
<Border />
|
||||||
|
<Border isBottom={true} />
|
||||||
|
<BannerContentWrapper>
|
||||||
|
<Column maxWidth="755px">
|
||||||
|
<CustomHeading>{heading}</CustomHeading>
|
||||||
|
|
||||||
|
{subline && (
|
||||||
|
<Paragraph color={colors.white} isMuted={0.5} isNoMargin={true}>
|
||||||
|
{subline}
|
||||||
|
</Paragraph>
|
||||||
|
)}
|
||||||
|
</Column>
|
||||||
|
<ColumnCta>
|
||||||
|
<ButtonWrap>
|
||||||
|
<Button
|
||||||
|
color={'rgba(255,255,255,0.6)'}
|
||||||
|
isTransparent={true}
|
||||||
|
isNoBorder={true}
|
||||||
|
borderColor={'rgba(0,0,0,0)'}
|
||||||
|
onClick={props.onDismiss}
|
||||||
|
>
|
||||||
|
Dismiss
|
||||||
|
</Button>
|
||||||
|
{mainCta && (
|
||||||
|
<Button
|
||||||
|
color={colors.white}
|
||||||
|
isTransparent={false}
|
||||||
|
href={mainCta.href}
|
||||||
|
onClick={mainCta.onClick}
|
||||||
|
target={mainCta.shouldOpenInNewTab ? '_blank' : ''}
|
||||||
|
>
|
||||||
|
{mainCta.text}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{secondaryCta && (
|
||||||
|
<Button
|
||||||
|
color={colors.white}
|
||||||
|
href={secondaryCta.href}
|
||||||
|
onClick={secondaryCta.onClick}
|
||||||
|
target={secondaryCta.shouldOpenInNewTab ? '_blank' : ''}
|
||||||
|
isTransparent={true}
|
||||||
|
>
|
||||||
|
{secondaryCta.text}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ButtonWrap>
|
||||||
|
</ColumnCta>
|
||||||
|
</BannerContentWrapper>
|
||||||
|
</CustomSection>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface CustomSectionProps extends SectionProps {
|
||||||
|
dismissed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BannerContentWrapper = styled.div`
|
||||||
|
max-width: 1200px;
|
||||||
|
display: flex;
|
||||||
|
margin: auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CustomSection = styled(Section)<CustomSectionProps>`
|
||||||
|
color: ${colors.white};
|
||||||
|
position: fixed;
|
||||||
|
height: ${ANNOUNCEMENT_BANNER_HEIGHT};
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1201;
|
||||||
|
padding: 0 1px;
|
||||||
|
margin: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
width: inherit;
|
||||||
|
transition: 300ms transform ease-in-out;
|
||||||
|
transform: translateY(-${props => props.dismissed ? '100%' : '0'});
|
||||||
|
font-family: Formular, sans-serif;
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ColumnCta = styled(Column)`
|
||||||
|
flex-shrink: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CustomHeading = styled.h2`
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ButtonWrap = styled.div`
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
* + * {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
a,
|
||||||
|
button {
|
||||||
|
display: block;
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* + * {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Note let's refactor this
|
||||||
|
// is it absolutely necessary to have a stateless component
|
||||||
|
// to pass props down into the styled icon?
|
||||||
|
const Border = styled.div<BorderProps>`
|
||||||
|
position: absolute;
|
||||||
|
background-image: ${props =>
|
||||||
|
props.isBottom ? 'url(/images/banner/bottomofcta.png);' : 'url(/images/banner/topofcta.png);'};
|
||||||
|
background-position: ${props => (props.isBottom ? 'left top' : 'left bottom')};
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 214px);
|
||||||
|
height: 40px;
|
||||||
|
top: ${props => !props.isBottom && 0};
|
||||||
|
bottom: ${props => props.isBottom && 0};
|
||||||
|
transform: translate(-112px);
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
width: calc(100% + 82px);
|
||||||
|
height: 40px;
|
||||||
|
transform: translate(-41px);
|
||||||
|
background-size: auto 80px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -72,8 +72,8 @@ const ButtonBase = styled.button<ButtonInterface>`
|
|||||||
background-color: ${props => (props.isTransparent || props.isWithArrow) && 'transparent'};
|
background-color: ${props => (props.isTransparent || props.isWithArrow) && 'transparent'};
|
||||||
border-color: ${props => props.isTransparent && !props.isWithArrow && props.borderColor};
|
border-color: ${props => props.isTransparent && !props.isWithArrow && props.borderColor};
|
||||||
color: ${props => (props.isAccentColor ? props.theme.linkColor : props.color || props.theme.textColor)};
|
color: ${props => (props.isAccentColor ? props.theme.linkColor : props.color || props.theme.textColor)};
|
||||||
padding: ${props => !props.isNoPadding && !props.isWithArrow && '18px 30px'};
|
padding: ${props =>
|
||||||
padding: ${props => !!props.padding && props.padding};
|
!props.isNoPadding && !props.isWithArrow && ((!!props.padding && props.padding) || '18px 30px')};
|
||||||
white-space: ${props => props.isWithArrow && 'nowrap'};
|
white-space: ${props => props.isWithArrow && 'nowrap'};
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: ${props => (props.isWithArrow ? '20px' : '18px')};
|
font-size: ${props => (props.isWithArrow ? '20px' : '18px')};
|
||||||
|
|||||||
@@ -79,7 +79,12 @@ class HeaderBase extends React.Component<HeaderProps> {
|
|||||||
const { isNavToggled, toggleMobileNav, theme } = this.props;
|
const { isNavToggled, toggleMobileNav, theme } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Headroom onUnpin={this.onUnpin} downTolerance={4} upTolerance={10}>
|
<Headroom
|
||||||
|
onUnpin={this.onUnpin}
|
||||||
|
downTolerance={4}
|
||||||
|
upTolerance={10}
|
||||||
|
wrapperStyle={{ position: 'relative', zIndex: 2 }}
|
||||||
|
>
|
||||||
<StyledHeader isNavToggled={isNavToggled}>
|
<StyledHeader isNavToggled={isNavToggled}>
|
||||||
<HeaderWrap>
|
<HeaderWrap>
|
||||||
<Link to={WebsitePaths.Home}>
|
<Link to={WebsitePaths.Home}>
|
||||||
@@ -94,7 +99,7 @@ class HeaderBase extends React.Component<HeaderProps> {
|
|||||||
|
|
||||||
<MediaQuery minWidth={990}>
|
<MediaQuery minWidth={990}>
|
||||||
<TradeButton bgColor={theme.headerButtonBg} color="#ffffff" href="/explore">
|
<TradeButton bgColor={theme.headerButtonBg} color="#ffffff" href="/explore">
|
||||||
Explore 0x
|
Trade on 0x
|
||||||
</TradeButton>
|
</TradeButton>
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
|
|
||||||
|
|||||||
@@ -93,8 +93,7 @@ const SectionBase = styled.section<SectionProps>`
|
|||||||
width: ${props => !props.isFullWidth && 'calc(100% - 60px)'};
|
width: ${props => !props.isFullWidth && 'calc(100% - 60px)'};
|
||||||
max-width: 1500px;
|
max-width: 1500px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: ${props => props.isPadded && '120px 0'};
|
padding: ${props => (!!props.padding && props.padding) || (props.isPadded && '120px 0')};
|
||||||
padding: ${props => !!props.padding && props.padding};
|
|
||||||
background-color: ${props => props.theme[`${props.bgColor}BgColor`] || props.bgColor};
|
background-color: ${props => props.theme[`${props.bgColor}BgColor`] || props.bgColor};
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: ${props => !props.isFullWidth && 'hidden'};
|
overflow: ${props => !props.isFullWidth && 'hidden'};
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const DEFAULT_MENU_THEME: MenuTheme = {
|
|||||||
|
|
||||||
export const Menu: React.StatelessComponent<MenuProps> = (props: MenuProps) => {
|
export const Menu: React.StatelessComponent<MenuProps> = (props: MenuProps) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style={{ paddingTop: 25 }}>
|
||||||
{_.map(props.menuItemEntries, entry => {
|
{_.map(props.menuItemEntries, entry => {
|
||||||
const isSelected = entry.to === props.selectedPath;
|
const isSelected = entry.to === props.selectedPath;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import { colors, Link } from '@0x/react-shared';
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
|
import { Redirect, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
import { Blockchain } from 'ts/blockchain';
|
import { Blockchain } from 'ts/blockchain';
|
||||||
|
import { ANNOUNCEMENT_BANNER_HEIGHT, AnnouncementBanner } from 'ts/components/annoucement_banner';
|
||||||
import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog';
|
import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog';
|
||||||
import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog';
|
import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog';
|
||||||
import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog';
|
import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog';
|
||||||
@@ -12,7 +13,6 @@ import { EthWrappers } from 'ts/components/eth_wrappers';
|
|||||||
import { FillOrder } from 'ts/components/fill_order';
|
import { FillOrder } from 'ts/components/fill_order';
|
||||||
import { AssetPicker } from 'ts/components/generate_order/asset_picker';
|
import { AssetPicker } from 'ts/components/generate_order/asset_picker';
|
||||||
import { MetaTags } from 'ts/components/meta_tags';
|
import { MetaTags } from 'ts/components/meta_tags';
|
||||||
import { BackButton } from 'ts/components/portal/back_button';
|
|
||||||
import { Loading } from 'ts/components/portal/loading';
|
import { Loading } from 'ts/components/portal/loading';
|
||||||
import { Menu, MenuTheme } from 'ts/components/portal/menu';
|
import { Menu, MenuTheme } from 'ts/components/portal/menu';
|
||||||
import { Section } from 'ts/components/portal/section';
|
import { Section } from 'ts/components/portal/section';
|
||||||
@@ -88,6 +88,7 @@ interface PortalState {
|
|||||||
isLedgerDialogOpen: boolean;
|
isLedgerDialogOpen: boolean;
|
||||||
tokenManagementState: TokenManagementState;
|
tokenManagementState: TokenManagementState;
|
||||||
trackedTokenStateByAddress: TokenStateByAddress;
|
trackedTokenStateByAddress: TokenStateByAddress;
|
||||||
|
dismissBanner: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AccountManagementItem {
|
interface AccountManagementItem {
|
||||||
@@ -133,6 +134,7 @@ export class Portal extends React.Component<PortalProps, PortalState> {
|
|||||||
isDisclaimerDialogOpen: !hasAcceptedDisclaimer,
|
isDisclaimerDialogOpen: !hasAcceptedDisclaimer,
|
||||||
tokenManagementState: TokenManagementState.None,
|
tokenManagementState: TokenManagementState.None,
|
||||||
isLedgerDialogOpen: false,
|
isLedgerDialogOpen: false,
|
||||||
|
dismissBanner: false,
|
||||||
trackedTokenStateByAddress: initialTrackedTokenStateByAddress,
|
trackedTokenStateByAddress: initialTrackedTokenStateByAddress,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -233,6 +235,18 @@ export class Portal extends React.Component<PortalProps, PortalState> {
|
|||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<MetaTags title={DOCUMENT_TITLE} description={DOCUMENT_DESCRIPTION} />
|
<MetaTags title={DOCUMENT_TITLE} description={DOCUMENT_DESCRIPTION} />
|
||||||
|
<AnnouncementBanner
|
||||||
|
dismissed={this.state.dismissBanner}
|
||||||
|
onDismiss={this._dismissBanner.bind(this)}
|
||||||
|
heading="Check out the new 0x Explore page"
|
||||||
|
subline="Need more advanced functionality? Try our code sandbox."
|
||||||
|
mainCta={{ text: 'Explore 0x', href: '/explore', shouldOpenInNewTab: true }}
|
||||||
|
secondaryCta={{
|
||||||
|
text: 'Code Sandbox',
|
||||||
|
href: 'https://codesandbox.io/s/1qmjyp7p5j',
|
||||||
|
shouldOpenInNewTab: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<TopBar
|
<TopBar
|
||||||
userAddress={this.props.userAddress}
|
userAddress={this.props.userAddress}
|
||||||
networkId={this.props.networkId}
|
networkId={this.props.networkId}
|
||||||
@@ -248,18 +262,22 @@ export class Portal extends React.Component<PortalProps, PortalState> {
|
|||||||
style={{
|
style={{
|
||||||
backgroundColor: colors.lightestGrey,
|
backgroundColor: colors.lightestGrey,
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
|
transition: '300ms top ease-in-out',
|
||||||
|
top: this.state.dismissBanner ? '0' : ANNOUNCEMENT_BANNER_HEIGHT,
|
||||||
zIndex: zIndex.topBar,
|
zIndex: zIndex.topBar,
|
||||||
}}
|
}}
|
||||||
maxWidth={LARGE_LAYOUT_MAX_WIDTH}
|
maxWidth={LARGE_LAYOUT_MAX_WIDTH}
|
||||||
/>
|
/>
|
||||||
<Container marginTop={TOP_BAR_HEIGHT} minHeight="100vh" backgroundColor={colors.lightestGrey}>
|
<Container
|
||||||
|
marginTop={`calc(${TOP_BAR_HEIGHT}px + ${
|
||||||
|
this.state.dismissBanner ? '0px' : ANNOUNCEMENT_BANNER_HEIGHT
|
||||||
|
})`}
|
||||||
|
minHeight="100vh"
|
||||||
|
backgroundColor={colors.lightestGrey}
|
||||||
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path={`${WebsitePaths.Portal}/:route`} render={this._renderOtherRoutes.bind(this)} />
|
<Route path={`${WebsitePaths.Portal}/:route`} render={this._renderOtherRoutes.bind(this)} />
|
||||||
<Route
|
<Redirect from={WebsitePaths.Portal} to={`/portal/account`} />
|
||||||
exact={true}
|
|
||||||
path={`${WebsitePaths.Portal}/`}
|
|
||||||
render={this._renderMainRoute.bind(this)}
|
|
||||||
/>
|
|
||||||
</Switch>
|
</Switch>
|
||||||
<BlockchainErrDialog
|
<BlockchainErrDialog
|
||||||
blockchain={this._blockchain}
|
blockchain={this._blockchain}
|
||||||
@@ -295,6 +313,11 @@ export class Portal extends React.Component<PortalProps, PortalState> {
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
private _dismissBanner(): void {
|
||||||
|
this.setState({dismissBanner: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-unused-variable
|
||||||
private _renderMainRoute(): React.ReactNode {
|
private _renderMainRoute(): React.ReactNode {
|
||||||
if (this._isSmallScreen()) {
|
if (this._isSmallScreen()) {
|
||||||
return <SmallLayout content={this._renderRelayerIndexSection()} />;
|
return <SmallLayout content={this._renderRelayerIndexSection()} />;
|
||||||
@@ -317,12 +340,7 @@ export class Portal extends React.Component<PortalProps, PortalState> {
|
|||||||
selectedIconColor: colors.yellow800,
|
selectedIconColor: colors.yellow800,
|
||||||
selectedBackgroundColor: 'transparent',
|
selectedBackgroundColor: 'transparent',
|
||||||
};
|
};
|
||||||
return (
|
return <Section body={<Menu selectedPath={routeComponentProps.location.pathname} theme={menuTheme} />} />;
|
||||||
<Section
|
|
||||||
header={<BackButton to={WebsitePaths.Portal} labelText="Back to Relayers" />}
|
|
||||||
body={<Menu selectedPath={routeComponentProps.location.pathname} theme={menuTheme} />}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
private _renderWallet(): React.ReactNode {
|
private _renderWallet(): React.ReactNode {
|
||||||
const isMobile = utils.isMobileWidth(this.props.screenWidth);
|
const isMobile = utils.isMobileWidth(this.props.screenWidth);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
export interface SectionProps {
|
export interface SectionProps {
|
||||||
header: React.ReactNode;
|
header?: React.ReactNode;
|
||||||
body: React.ReactNode;
|
body: React.ReactNode;
|
||||||
}
|
}
|
||||||
export const Section = (props: SectionProps) => {
|
export const Section = (props: SectionProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-column">
|
<div className="flex flex-column">
|
||||||
{props.header}
|
{!!props.header && props.header}
|
||||||
{props.body}
|
{props.body}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,22 +7,24 @@ interface InputProps {
|
|||||||
name?: string;
|
name?: string;
|
||||||
width?: string;
|
width?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
value?: string;
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
onChange?: (e: React.ChangeEvent) => void;
|
onChange?: (e: React.ChangeEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Input = React.forwardRef((props: InputProps, ref?: React.Ref<HTMLInputElement>) => {
|
export const Input = React.forwardRef((props: InputProps, ref?: React.Ref<HTMLInputElement>) => {
|
||||||
const { name, type, placeholder, defaultValue, onChange, width, className } = props;
|
const { name, type, placeholder, defaultValue, onChange, width, className, value } = props;
|
||||||
const componentType = type === 'textarea' ? 'textarea' : 'input';
|
const componentType = type === 'textarea' ? 'textarea' : 'input';
|
||||||
const inputProps = { name, type };
|
const inputProps = { name, type };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputWrapper className={className} width={width}>
|
<InputWrapper className={className} width={width}>
|
||||||
<Icon size={20} name="search" />
|
<Icon size={18} name="search" />
|
||||||
<StyledInput
|
<StyledInput
|
||||||
as={componentType}
|
as={componentType}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
value={value}
|
||||||
id={`input-${name}`}
|
id={`input-${name}`}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
@@ -37,8 +39,8 @@ const StyledInput = styled.input`
|
|||||||
appearance: none;
|
appearance: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: #000;
|
color: #000;
|
||||||
font-size: 1.294117647rem;
|
font-size: 22px;
|
||||||
padding: 16px 15px 14px;
|
padding: 10px 12px 9px;
|
||||||
outline: none;
|
outline: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: ${props => props.type === 'textarea' && `120px`};
|
min-height: ${props => props.type === 'textarea' && `120px`};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<line x1="11.1228" y1="11.1849" x2="19.5844" y2="19.6464" stroke="black"/>
|
<path d="M1 7.52174C1 11.1251 3.91842 14.0435 7.52174 14.0435C11.1251 14.0435 14.0435 11.1251 14.0435 7.52174C14.0435 3.9795 11.221 1.09597 7.7006 1.00436C7.63516 1.00436 7.57409 1 7.50865 1C3.91406 1.00436 1 3.92279 1 7.52174Z" stroke="#B1B1B1" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
|
||||||
<circle cx="6.53846" cy="6.53846" r="6.03846" stroke="black"/>
|
<path d="M12 12L19 19" stroke="#B1B1B1" stroke-width="2"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 474 B |
@@ -14,7 +14,10 @@ import { BY_NAME_ORDERINGS, EDITORIAL, FILTERS, ORDERINGS, PROJECTS } from 'ts/p
|
|||||||
import { ExploreSettingsDropdown } from 'ts/pages/explore/explore_dropdown';
|
import { ExploreSettingsDropdown } from 'ts/pages/explore/explore_dropdown';
|
||||||
import { ExploreGrid } from 'ts/pages/explore/explore_grid';
|
import { ExploreGrid } from 'ts/pages/explore/explore_grid';
|
||||||
import { EXPLORE_STATE_DIALOGS, ExploreGridDialogTile } from 'ts/pages/explore/explore_grid_state_tile';
|
import { EXPLORE_STATE_DIALOGS, ExploreGridDialogTile } from 'ts/pages/explore/explore_grid_state_tile';
|
||||||
import { Button as ExploreTagButton } from 'ts/pages/explore/explore_tag_button';
|
import { ExploreTagButton } from 'ts/pages/explore/explore_tag_button';
|
||||||
|
import { analytics } from 'ts/utils/analytics';
|
||||||
|
import { constants } from 'ts/utils/constants';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ExploreAnalyticAction,
|
ExploreAnalyticAction,
|
||||||
ExploreFilterMetadata,
|
ExploreFilterMetadata,
|
||||||
@@ -43,34 +46,36 @@ export class Explore extends React.Component<ExploreProps> {
|
|||||||
isTilesLoading: false,
|
isTilesLoading: false,
|
||||||
isContactModalOpen: false,
|
isContactModalOpen: false,
|
||||||
tiles: [] as ExploreTile[],
|
tiles: [] as ExploreTile[],
|
||||||
tilesOrdering: ExploreTilesOrdering.None,
|
tilesOrdering: ExploreTilesOrdering.Popular,
|
||||||
isEditorialShown: true,
|
isEditorialShown: true,
|
||||||
filters: FILTERS,
|
filters: FILTERS,
|
||||||
query: '',
|
query: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly _debouncedChangeSearchResults: (query: string) => void;
|
||||||
|
|
||||||
constructor(props: ExploreProps) {
|
constructor(props: ExploreProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this._debouncedChangeSearchResults = _.debounce(this._changeSearchResults, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillMount(): void {
|
// tslint:disable-next-line:async-suffix
|
||||||
// tslint:disable-next-line:no-floating-promises
|
public async componentDidMount(): Promise<void> {
|
||||||
this._loadEntriesAsync().then(() => {
|
await this._loadEntriesAsync();
|
||||||
this._setFilter('all');
|
await this._setFilter('all');
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<SiteWrap theme="light">
|
<SiteWrap theme="light">
|
||||||
<DocumentTitle {...documentConstants.EXPLORE} />
|
<DocumentTitle {...documentConstants.EXPLORE} />
|
||||||
<ExploreHero onSearch={this._changeSearchResults} />
|
<ExploreHero query={this.state.query} onSearch={this._setNewQuery} />
|
||||||
<Section padding={'0 0 120px 0'} maxWidth={'1150px'}>
|
<Section padding={'0 0 60px 0'} maxWidth={'1150px'}>
|
||||||
<ExploreToolBar
|
<ExploreToolBar
|
||||||
onFilterClick={this._setFilter}
|
onFilterClick={this._setFilter}
|
||||||
filters={this.state.filters}
|
filters={this.state.filters}
|
||||||
editorial={this.state.isEditorialShown}
|
// editorial={this.state.isEditorialShown}
|
||||||
onEditorial={this._onEditorial}
|
// onEditorial={this._onEditorial}
|
||||||
orderings={ORDERINGS}
|
orderings={ORDERINGS}
|
||||||
activeOrdering={this.state.tilesOrdering}
|
activeOrdering={this.state.tilesOrdering}
|
||||||
onOrdering={this._onOrdering}
|
onOrdering={this._onOrdering}
|
||||||
@@ -81,7 +86,7 @@ export class Explore extends React.Component<ExploreProps> {
|
|||||||
heading="Working on a 0x project?"
|
heading="Working on a 0x project?"
|
||||||
subline="Lorem Ipsum something then that and say something more."
|
subline="Lorem Ipsum something then that and say something more."
|
||||||
mainCta={{ text: 'Apply Now', onClick: this._onOpenContactModal }}
|
mainCta={{ text: 'Apply Now', onClick: this._onOpenContactModal }}
|
||||||
secondaryCta={{ text: 'Join Discord', href: 'https://discordapp.com/invite/d3FTX3M' }}
|
secondaryCta={{ text: 'Join Discord', href: constants.URL_ZEROEX_CHAT }}
|
||||||
/>
|
/>
|
||||||
<ModalContact
|
<ModalContact
|
||||||
isOpen={this.state.isContactModalOpen}
|
isOpen={this.state.isContactModalOpen}
|
||||||
@@ -100,23 +105,21 @@ export class Explore extends React.Component<ExploreProps> {
|
|||||||
this.setState({ isContactModalOpen: false });
|
this.setState({ isContactModalOpen: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _onEditorial = (newValue: boolean): void => {
|
// tslint:disable-next-line:no-unused-variable
|
||||||
// tslint:disable-next-line:no-floating-promises
|
private readonly _onEditorial = async (newValue: boolean): Promise<void> => {
|
||||||
this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Editorial, {
|
const newTiles = await this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Editorial, {
|
||||||
isEditorialShown: newValue,
|
isEditorialShown: newValue,
|
||||||
}).then(newTiles => {
|
|
||||||
this.setState({ isEditorialShown: newValue, tiles: newTiles });
|
|
||||||
});
|
});
|
||||||
|
this.setState({ isEditorialShown: newValue, tiles: newTiles });
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _onOrdering = (newValue: string) => {
|
private readonly _onOrdering = async (newValue: string): Promise<void> => {
|
||||||
this.setState({ tilesOrdering: newValue });
|
this.setState({ tilesOrdering: newValue });
|
||||||
// tslint:disable-next-line:no-floating-promises
|
// tslint:disable-next-line:no-floating-promises
|
||||||
this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Ordering, {
|
const newTiles = await this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Ordering, {
|
||||||
tilesOrdering: newValue as ExploreTilesOrdering,
|
tilesOrdering: newValue as ExploreTilesOrdering,
|
||||||
}).then(newTiles => {
|
|
||||||
this.setState({ tilesOrdering: newValue, tiles: newTiles });
|
|
||||||
});
|
});
|
||||||
|
this.setState({ tilesOrdering: newValue, tiles: newTiles });
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _launchInstantAsync = (params: ExploreProjectInstantMetadata): void => {
|
private readonly _launchInstantAsync = (params: ExploreProjectInstantMetadata): void => {
|
||||||
@@ -125,6 +128,16 @@ export class Explore extends React.Component<ExploreProps> {
|
|||||||
|
|
||||||
private readonly _onAnalytics = (project: ExploreProject, action: ExploreAnalyticAction): void => {
|
private readonly _onAnalytics = (project: ExploreProject, action: ExploreAnalyticAction): void => {
|
||||||
// Do Something
|
// Do Something
|
||||||
|
switch (action) {
|
||||||
|
case ExploreAnalyticAction.InstantClick:
|
||||||
|
analytics.track('Explore - Instant - Clicked', { name: project.name });
|
||||||
|
break;
|
||||||
|
case ExploreAnalyticAction.LinkClick:
|
||||||
|
analytics.track('Explore - Link - Clicked', { name: project.name });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _generateEditorialContent(): void {
|
private _generateEditorialContent(): void {
|
||||||
@@ -155,24 +168,24 @@ export class Explore extends React.Component<ExploreProps> {
|
|||||||
return this.state.tiles;
|
return this.state.tiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _changeSearchResults = (query: string): void => {
|
private readonly _setNewQuery = (query: string): void => {
|
||||||
const trimmedQuery = query.trim().toLowerCase();
|
this.setState({ query });
|
||||||
// tslint:disable-next-line:no-floating-promises
|
this._debouncedChangeSearchResults(query);
|
||||||
this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Search, {
|
|
||||||
query: trimmedQuery,
|
|
||||||
filter: this.state.filters.find(f => f.active),
|
|
||||||
})
|
|
||||||
.then(async newTiles =>
|
|
||||||
this._generateTilesWithModifier(newTiles, ExploreTilesModifiers.Editorial, {
|
|
||||||
isEditorialShown: _.isEmpty(trimmedQuery) ? this.state.isEditorialShown : false,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.then(newTiles => {
|
|
||||||
this.setState({ query: trimmedQuery, tiles: newTiles });
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _setFilter = (filterName: string, active: boolean = true): void => {
|
private readonly _changeSearchResults = async (query: string): Promise<void> => {
|
||||||
|
// tslint:disable-next-line:no-floating-promises
|
||||||
|
const searchedTiles = await this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Search, {
|
||||||
|
query,
|
||||||
|
filter: this.state.filters.find(f => f.active),
|
||||||
|
});
|
||||||
|
// const newTiles = this._generateTilesWithModifier(searchedTiles, ExploreTilesModifiers.Editorial, {
|
||||||
|
// isEditorialShown: _.isEmpty(query) ? this.state.isEditorialShown : false,
|
||||||
|
// })
|
||||||
|
this.setState({ tiles: searchedTiles });
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly _setFilter = async (filterName: string, active: boolean = true): Promise<void> => {
|
||||||
let updatedFilters: ExploreFilterMetadata[];
|
let updatedFilters: ExploreFilterMetadata[];
|
||||||
updatedFilters = this.state.filters.map(f => {
|
updatedFilters = this.state.filters.map(f => {
|
||||||
const newFilter = _.assign({}, f);
|
const newFilter = _.assign({}, f);
|
||||||
@@ -181,14 +194,18 @@ export class Explore extends React.Component<ExploreProps> {
|
|||||||
});
|
});
|
||||||
// If no filters are enabled, default to all
|
// If no filters are enabled, default to all
|
||||||
if (_.filter(updatedFilters, f => f.active).length === 0) {
|
if (_.filter(updatedFilters, f => f.active).length === 0) {
|
||||||
this._setFilter('all');
|
await this._setFilter('all');
|
||||||
} else {
|
} else {
|
||||||
// tslint:disable-next-line:no-floating-promises
|
// tslint:disable-next-line:no-floating-promises
|
||||||
this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Filter, {
|
const newTiles = await this._generateTilesWithModifier(
|
||||||
filter: updatedFilters.find(f => f.active),
|
this.state.tiles,
|
||||||
}).then(newTiles => {
|
_.isEmpty(this.state.query) ? ExploreTilesModifiers.Filter : ExploreTilesModifiers.Search,
|
||||||
this.setState({ filters: updatedFilters, tiles: newTiles });
|
{
|
||||||
});
|
filter: updatedFilters.find(f => f.active),
|
||||||
|
query: this.state.query,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this.setState({ filters: updatedFilters, tiles: newTiles });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -216,6 +233,7 @@ export class Explore extends React.Component<ExploreProps> {
|
|||||||
modifier: ExploreTilesModifiers,
|
modifier: ExploreTilesModifiers,
|
||||||
options: ExploreModifierOptions,
|
options: ExploreModifierOptions,
|
||||||
): Promise<ExploreTile[]> => {
|
): Promise<ExploreTile[]> => {
|
||||||
|
const trimmedQuery = modifier === ExploreTilesModifiers.Search ? options.query.trim().toLowerCase() : '';
|
||||||
if (!this._verifyExploreTilesModifierOptions(modifier, options)) {
|
if (!this._verifyExploreTilesModifierOptions(modifier, options)) {
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
@@ -244,7 +262,7 @@ export class Explore extends React.Component<ExploreProps> {
|
|||||||
newTile.visibility === ExploreTileVisibility.Visible
|
newTile.visibility === ExploreTileVisibility.Visible
|
||||||
) {
|
) {
|
||||||
newTile.visibility =
|
newTile.visibility =
|
||||||
(_.startsWith(newTile.exploreProject.label.toLowerCase(), options.query) &&
|
(_.startsWith(newTile.exploreProject.label.toLowerCase(), trimmedQuery) &&
|
||||||
ExploreTileVisibility.Visible) ||
|
ExploreTileVisibility.Visible) ||
|
||||||
ExploreTileVisibility.Hidden;
|
ExploreTileVisibility.Hidden;
|
||||||
}
|
}
|
||||||
@@ -280,7 +298,7 @@ export class Explore extends React.Component<ExploreProps> {
|
|||||||
tilesOrdering: this.state.tilesOrdering,
|
tilesOrdering: this.state.tilesOrdering,
|
||||||
});
|
});
|
||||||
this.setState({ tiles: orderedTiles, isEntriesLoading: false }, () => {
|
this.setState({ tiles: orderedTiles, isEntriesLoading: false }, () => {
|
||||||
this._generateEditorialContent();
|
// this._generateEditorialContent();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -292,22 +310,21 @@ const ExploreHeroContentWrapper = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
interface ExploreHeroProps {
|
interface ExploreHeroProps {
|
||||||
|
query: string;
|
||||||
onSearch(query: string): void;
|
onSearch(query: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExploreHero = (props: ExploreHeroProps) => {
|
const ExploreHero = (props: ExploreHeroProps) => {
|
||||||
// tslint:disable-next-line:no-unbound-method
|
|
||||||
const onSearchDebounce = _.debounce(props.onSearch, 300);
|
|
||||||
const onChange = (e: any) => {
|
const onChange = (e: any) => {
|
||||||
onSearchDebounce(e.target.value);
|
props.onSearch(e.target.value);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Section maxWidth={'1150px'}>
|
<Section maxWidth={'1150px'} padding={'50px 0 50px 0'}>
|
||||||
<ExploreHeroContentWrapper>
|
<ExploreHeroContentWrapper>
|
||||||
<Heading isNoMargin={true} size="large">
|
<Heading isNoMargin={true} size="large">
|
||||||
Explore 0x
|
Explore 0x
|
||||||
</Heading>
|
</Heading>
|
||||||
<SearchInput onChange={onChange} width={'28rem'} placeholder="Search tokens, relayers, and dApps..." />
|
<SearchInput value={props.query} onChange={onChange} width={'22rem'} placeholder="Search..." />
|
||||||
</ExploreHeroContentWrapper>
|
</ExploreHeroContentWrapper>
|
||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
@@ -320,7 +337,7 @@ const ExploreToolBarWrapper = styled.div`
|
|||||||
|
|
||||||
const ExploreToolBarContentWrapper = styled.div`
|
const ExploreToolBarContentWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 2rem 0;
|
padding-bottom: 2rem;
|
||||||
& > * {
|
& > * {
|
||||||
margin: 0 0.3rem;
|
margin: 0 0.3rem;
|
||||||
}
|
}
|
||||||
@@ -336,9 +353,9 @@ interface ExploreToolBarProps {
|
|||||||
filters: ExploreFilterMetadata[];
|
filters: ExploreFilterMetadata[];
|
||||||
activeOrdering: ExploreTilesOrdering;
|
activeOrdering: ExploreTilesOrdering;
|
||||||
orderings: ExploreTilesOrderingMetadata[];
|
orderings: ExploreTilesOrderingMetadata[];
|
||||||
editorial: boolean;
|
editorial?: boolean;
|
||||||
onOrdering: (newValue: string) => void;
|
onOrdering: (newValue: string) => void;
|
||||||
onEditorial: (newValue: boolean) => void;
|
onEditorial?: (newValue: boolean) => void;
|
||||||
onFilterClick(filterName: string, active: boolean): void;
|
onFilterClick(filterName: string, active: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,9 +375,7 @@ const ExploreToolBar = (props: ExploreToolBarProps) => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ExploreToolBarContentWrapper>
|
</ExploreToolBarContentWrapper>
|
||||||
<ExploreToolBarContentWrapper>
|
<ExploreSettingsDropdown {...props} />
|
||||||
<ExploreSettingsDropdown {...props} />
|
|
||||||
</ExploreToolBarContentWrapper>
|
|
||||||
</ExploreToolBarWrapper>
|
</ExploreToolBarWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -99,23 +99,30 @@ export const FILTERS: ExploreFilterMetadata[] = [
|
|||||||
label: 'Relayer',
|
label: 'Relayer',
|
||||||
name: 'relayer',
|
name: 'relayer',
|
||||||
filterType: ExploreFilterType.Keyword,
|
filterType: ExploreFilterType.Keyword,
|
||||||
keyword: 'relayer',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Collectibles',
|
label: 'Collectibles',
|
||||||
name: 'collectibles',
|
name: 'collectibles',
|
||||||
filterType: ExploreFilterType.Keyword,
|
filterType: ExploreFilterType.Keyword,
|
||||||
keyword: 'ERC-721',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ORDERINGS: ExploreTilesOrderingMetadata[] = [
|
export const ORDERINGS: ExploreTilesOrderingMetadata[] = [
|
||||||
{ label: 'None', ordering: ExploreTilesOrdering.None, type: ExploreTilesOrderingType.HardCodedByName },
|
|
||||||
{ label: 'Latest', ordering: ExploreTilesOrdering.Latest, type: ExploreTilesOrderingType.HardCodedByName },
|
|
||||||
{ label: 'Popular', ordering: ExploreTilesOrdering.Popular, type: ExploreTilesOrderingType.HardCodedByName },
|
{ label: 'Popular', ordering: ExploreTilesOrdering.Popular, type: ExploreTilesOrderingType.HardCodedByName },
|
||||||
|
{
|
||||||
|
label: 'Recently Added',
|
||||||
|
ordering: ExploreTilesOrdering.RecentlyAdded,
|
||||||
|
type: ExploreTilesOrderingType.HardCodedByName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Alphabetical',
|
||||||
|
ordering: ExploreTilesOrdering.Alphabetical,
|
||||||
|
type: ExploreTilesOrderingType.HardCodedByName,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const BY_NAME_ORDERINGS: { [s: string]: string[] } = {
|
export const BY_NAME_ORDERINGS: { [s: string]: string[] } = {
|
||||||
[ExploreTilesOrdering.Popular]: ['boxswap', 'veil', 'paradex', 'emoon', 'radar_relay', 'openrelay'],
|
[ExploreTilesOrdering.Popular]: ['boxswap', 'veil', 'paradex', 'emoon', 'radar_relay', 'openrelay'],
|
||||||
[ExploreTilesOrdering.Latest]: ['veil', 'boxswap', 'emoon', 'paradex', 'radar_relay', 'openrelay'],
|
[ExploreTilesOrdering.RecentlyAdded]: ['veil', 'boxswap', 'emoon', 'paradex', 'radar_relay', 'openrelay'],
|
||||||
|
[ExploreTilesOrdering.Alphabetical]: ['veil', 'boxswap', 'emoon', 'paradex', 'radar_relay', 'openrelay'],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import styled from 'styled-components';
|
|||||||
import { Icon } from 'ts/components/icon';
|
import { Icon } from 'ts/components/icon';
|
||||||
import { Heading } from 'ts/components/text';
|
import { Heading } from 'ts/components/text';
|
||||||
import { Switch } from 'ts/components/ui/switch';
|
import { Switch } from 'ts/components/ui/switch';
|
||||||
import { Button as ExploreTagButton } from 'ts/pages/explore/explore_tag_button';
|
import { ExploreTagButton } from 'ts/pages/explore/explore_tag_button';
|
||||||
import { colors } from 'ts/style/colors';
|
import { colors } from 'ts/style/colors';
|
||||||
import { ExploreTilesOrdering, ExploreTilesOrderingMetadata } from 'ts/types';
|
import { ExploreTilesOrdering, ExploreTilesOrderingMetadata } from 'ts/types';
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ const ExploreSettingsDropdownButton = ({}) => {
|
|||||||
<SettingsIconWrapper>
|
<SettingsIconWrapper>
|
||||||
<Icon color={colors.grey} name="settings" size={16} />
|
<Icon color={colors.grey} name="settings" size={16} />
|
||||||
</SettingsIconWrapper>
|
</SettingsIconWrapper>
|
||||||
Settings
|
Sort
|
||||||
</ExploreTagButton>
|
</ExploreTagButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -65,7 +65,7 @@ interface DropdownWrapInterface {
|
|||||||
|
|
||||||
const DropdownWrap = styled.div<DropdownWrapInterface>`
|
const DropdownWrap = styled.div<DropdownWrapInterface>`
|
||||||
width: ${props => props.width || 280}px;
|
width: ${props => props.width || 280}px;
|
||||||
margin-top: 16px;
|
margin-top: calc(16px - 2rem);
|
||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-color: ${props => props.theme.dropdownBorderColor};
|
border-color: ${props => props.theme.dropdownBorderColor};
|
||||||
@@ -107,27 +107,29 @@ const DropdownWrap = styled.div<DropdownWrapInterface>`
|
|||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
// display: block;
|
||||||
|
// visibility: visible;
|
||||||
|
// opacity: 1;
|
||||||
|
// transform: translate3d(0, 0, 0);
|
||||||
|
// transition: opacity 0.35s, transform 0.35s, visibility 0s;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export interface ExploreSettingsDropdownProps {
|
export interface ExploreSettingsDropdownProps {
|
||||||
activeOrdering: ExploreTilesOrdering;
|
activeOrdering: ExploreTilesOrdering;
|
||||||
orderings: ExploreTilesOrderingMetadata[];
|
orderings: ExploreTilesOrderingMetadata[];
|
||||||
onOrdering: (newValue: string) => void;
|
onOrdering: (newValue: string) => void;
|
||||||
onEditorial: (newValue: boolean) => void;
|
onEditorial?: (newValue: boolean) => void;
|
||||||
editorial: boolean;
|
editorial?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExploreSettingsDropdown extends React.Component<ExploreSettingsDropdownProps> {
|
export class ExploreSettingsDropdown extends React.Component<ExploreSettingsDropdownProps> {
|
||||||
constructor(props: ExploreSettingsDropdownProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<SettingsWrapper>
|
<SettingsWrapper>
|
||||||
<ExploreSettingsDropdownButton />
|
<ExploreSettingsDropdownButton />
|
||||||
<DropdownWrap>
|
<DropdownWrap>
|
||||||
<DropdownContentWrapper>
|
<DropdownContentWrapper>
|
||||||
|
{!!this.props.onEditorial &&
|
||||||
<div>
|
<div>
|
||||||
<Switch
|
<Switch
|
||||||
isChecked={this.props.editorial}
|
isChecked={this.props.editorial}
|
||||||
@@ -137,11 +139,11 @@ export class ExploreSettingsDropdown extends React.Component<ExploreSettingsDrop
|
|||||||
<Heading asElement="h4" size={14} color="inherit" marginBottom="0" isMuted={0.35}>
|
<Heading asElement="h4" size={14} color="inherit" marginBottom="0" isMuted={0.35}>
|
||||||
Editorial content reflects the views of the 0x core team.
|
Editorial content reflects the views of the 0x core team.
|
||||||
</Heading>
|
</Heading>
|
||||||
</div>
|
</div>}
|
||||||
<OrderingWrapper>
|
<OrderingWrapper>
|
||||||
<Heading asElement="h4" size={14} color="inherit" marginBottom="16px" isMuted={0.35}>
|
{/* <Heading asElement="h4" size={14} color="inherit" marginBottom="16px" isMuted={0.35}>
|
||||||
Ordering
|
Ordering
|
||||||
</Heading>
|
</Heading> */}
|
||||||
<OrderingListWrapper>
|
<OrderingListWrapper>
|
||||||
{this.props.orderings.map(o => {
|
{this.props.orderings.map(o => {
|
||||||
const onClick = () => this.props.onOrdering(o.ordering);
|
const onClick = () => this.props.onOrdering(o.ordering);
|
||||||
@@ -167,19 +169,18 @@ export class ExploreSettingsDropdown extends React.Component<ExploreSettingsDrop
|
|||||||
const DropdownContentWrapper = styled.div``;
|
const DropdownContentWrapper = styled.div``;
|
||||||
|
|
||||||
const OrderingWrapper = styled.div`
|
const OrderingWrapper = styled.div`
|
||||||
padding-top: 20px;
|
margin-top: 10px;
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
// padding-top: 20px;
|
||||||
&:before {
|
// margin-bottom: 20px;
|
||||||
content: '';
|
// &:before {
|
||||||
width: 100%;
|
// content: '';
|
||||||
height: 1px;
|
// width: 100%;
|
||||||
background-color: ${props => props.theme.dropdownColor};
|
// height: 1px;
|
||||||
opacity: 0.15;
|
// background-color: ${props => props.theme.dropdownColor};
|
||||||
position: absolute;
|
// opacity: 0.15;
|
||||||
top: 0;
|
// position: absolute;
|
||||||
left: 0;
|
// top: 0;
|
||||||
}
|
// left: 0;
|
||||||
|
// }
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ interface RicherExploreGridListTile extends ExploreTile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ExploreGrid extends React.Component<ExptoreGridProps> {
|
export class ExploreGrid extends React.Component<ExptoreGridProps> {
|
||||||
constructor(props: ExptoreGridProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<ExploreGridList>
|
<ExploreGridList>
|
||||||
@@ -79,6 +75,9 @@ const ExploreGridList = styled.div<ExploreGridListProps>`
|
|||||||
grid-template-columns: repeat(${ExploreTileWidth.FullWidth}, 1fr);
|
grid-template-columns: repeat(${ExploreTileWidth.FullWidth}, 1fr);
|
||||||
grid-column-gap: 1.5rem;
|
grid-column-gap: 1.5rem;
|
||||||
grid-row-gap: 1.5rem;
|
grid-row-gap: 1.5rem;
|
||||||
|
& > * {
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
@media (max-width: 56rem) {
|
@media (max-width: 56rem) {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import * as _ from 'lodash';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@@ -6,11 +7,9 @@ import { Heading, Paragraph } from 'ts/components/text';
|
|||||||
import { Image } from 'ts/components/ui/image';
|
import { Image } from 'ts/components/ui/image';
|
||||||
import { ExploreAnalyticAction, ExploreProject } from 'ts/types';
|
import { ExploreAnalyticAction, ExploreProject } from 'ts/types';
|
||||||
|
|
||||||
const EMPTY_FUNCTION = () => true;
|
|
||||||
|
|
||||||
export const ExploreGridTile = (props: ExploreProject) => {
|
export const ExploreGridTile = (props: ExploreProject) => {
|
||||||
// tslint:disable-next-line:no-unbound-method
|
// tslint:disable-next-line:no-unbound-method
|
||||||
const onAnalytics = props.onAnalytics || EMPTY_FUNCTION;
|
const onAnalytics = props.onAnalytics || _.noop;
|
||||||
const onInstantClick = !!props.instant
|
const onInstantClick = !!props.instant
|
||||||
? () => {
|
? () => {
|
||||||
props.onInstantClick();
|
props.onInstantClick();
|
||||||
@@ -21,9 +20,9 @@ export const ExploreGridTile = (props: ExploreProject) => {
|
|||||||
onAnalytics(ExploreAnalyticAction.LinkClick);
|
onAnalytics(ExploreAnalyticAction.LinkClick);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<ExploreGridTileWrapper>
|
<ExploreGridTileWrapper style={{ border: 'none' }}>
|
||||||
{!!onInstantClick && (
|
{!!onInstantClick && (
|
||||||
<ExploreGridButtonWrapper>
|
<ExploreGridButtonWrapper className="explore-grid-instant-button-wrapper">
|
||||||
<Button onClick={onInstantClick} padding={'12px 18px'} bgColor={'white'}>
|
<Button onClick={onInstantClick} padding={'12px 18px'} bgColor={'white'}>
|
||||||
Instant Trade
|
Instant Trade
|
||||||
</Button>
|
</Button>
|
||||||
@@ -59,21 +58,32 @@ const ExploreGridHeroWell = styled.div<ExploreGridHeroWellProps>`
|
|||||||
|
|
||||||
const ExploreGridContentWell = styled.div`
|
const ExploreGridContentWell = styled.div`
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||||
|
border-top: 0;
|
||||||
|
flex-grow: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ExploreGridTileLink = styled.a`
|
const ExploreGridTileLink = styled.a`
|
||||||
display: block;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ExploreGridTileWrapper = styled.div`
|
export const ExploreGridTileWrapper = styled.div`
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
height: 100%;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||||
// transition: box-shadow 200ms ease-in-out;
|
& .explore-grid-instant-button-wrapper {
|
||||||
// &:hover {
|
transition: opacity 0.2s, visibility 0.2s 0s;
|
||||||
// box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.1);
|
opacity: 0;
|
||||||
// }
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
&:hover .explore-grid-instant-button-wrapper {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ExploreGridButtonWrapper = styled.div`
|
const ExploreGridButtonWrapper = styled.div`
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export interface ButtonInterface {
|
|||||||
theme?: ThemeInterface;
|
theme?: ThemeInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Button: React.StatelessComponent<ButtonInterface> = (props: ButtonInterface) => {
|
export const ExploreTagButton: React.StatelessComponent<ButtonInterface> = (props: ButtonInterface) => {
|
||||||
const { children, isDisabled, className } = props;
|
const { children, isDisabled, className } = props;
|
||||||
|
|
||||||
const buttonProps = { disabled: isDisabled };
|
const buttonProps = { disabled: isDisabled };
|
||||||
@@ -29,7 +29,7 @@ export const Button: React.StatelessComponent<ButtonInterface> = (props: ButtonI
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Button.defaultProps = {};
|
ExploreTagButton.defaultProps = {};
|
||||||
|
|
||||||
const ButtonBase = styled.button<ButtonInterface>`
|
const ButtonBase = styled.button<ButtonInterface>`
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
|||||||
@@ -265,8 +265,8 @@ export enum ExploreAnalyticAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum ExploreTilesOrdering {
|
export enum ExploreTilesOrdering {
|
||||||
None = 'NONE',
|
Alphabetical = 'ALPHABETICAL',
|
||||||
Latest = 'LATEST',
|
RecentlyAdded = 'RECENTLY_ADDED',
|
||||||
Popular = 'POPULAR',
|
Popular = 'POPULAR',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +297,6 @@ export interface ExploreFilterMetadata {
|
|||||||
label: string;
|
label: string;
|
||||||
filterType: ExploreFilterType;
|
filterType: ExploreFilterType;
|
||||||
name: string;
|
name: string;
|
||||||
keyword?: string;
|
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user