From 3ed2c732bd8af69ca1f5ea7df168c4f8654f6dd4 Mon Sep 17 00:00:00 2001 From: David Sun Date: Fri, 15 Mar 2019 12:44:55 -0400 Subject: [PATCH] added announcement banner --- .../ts/components/annoucement_banner.tsx | 195 ++++++++++++++++++ packages/website/ts/components/button.tsx | 4 +- packages/website/ts/components/header.tsx | 9 +- packages/website/ts/components/newLayout.tsx | 3 +- .../website/ts/components/portal/menu.tsx | 2 +- .../website/ts/components/portal/portal.tsx | 46 +++-- .../website/ts/components/portal/section.tsx | 4 +- .../ts/components/ui/search_textfield.tsx | 10 +- .../website/ts/icons/illustrations/search.svg | 4 +- packages/website/ts/pages/explore.tsx | 127 +++++++----- .../ts/pages/explore/explore_content.tsx | 17 +- .../ts/pages/explore/explore_dropdown.tsx | 53 ++--- .../website/ts/pages/explore/explore_grid.tsx | 7 +- .../ts/pages/explore/explore_grid_tile.tsx | 30 ++- .../ts/pages/explore/explore_tag_button.tsx | 4 +- packages/website/ts/types.ts | 5 +- 16 files changed, 385 insertions(+), 135 deletions(-) create mode 100644 packages/website/ts/components/annoucement_banner.tsx diff --git a/packages/website/ts/components/annoucement_banner.tsx b/packages/website/ts/components/annoucement_banner.tsx new file mode 100644 index 0000000000..aae1bcfc50 --- /dev/null +++ b/packages/website/ts/components/annoucement_banner.tsx @@ -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) => { + const { heading, subline, mainCta, secondaryCta } = props; + return ( + + + + + + {heading} + + {subline && ( + + {subline} + + )} + + + + + {mainCta && ( + + )} + + {secondaryCta && ( + + )} + + + + + ); +}; + +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)` + 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` + 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; + } +`; diff --git a/packages/website/ts/components/button.tsx b/packages/website/ts/components/button.tsx index d1f1cdcea4..3d1cc38bb1 100644 --- a/packages/website/ts/components/button.tsx +++ b/packages/website/ts/components/button.tsx @@ -72,8 +72,8 @@ const ButtonBase = styled.button` background-color: ${props => (props.isTransparent || props.isWithArrow) && 'transparent'}; border-color: ${props => props.isTransparent && !props.isWithArrow && props.borderColor}; color: ${props => (props.isAccentColor ? props.theme.linkColor : props.color || props.theme.textColor)}; - padding: ${props => !props.isNoPadding && !props.isWithArrow && '18px 30px'}; - padding: ${props => !!props.padding && props.padding}; + padding: ${props => + !props.isNoPadding && !props.isWithArrow && ((!!props.padding && props.padding) || '18px 30px')}; white-space: ${props => props.isWithArrow && 'nowrap'}; text-align: center; font-size: ${props => (props.isWithArrow ? '20px' : '18px')}; diff --git a/packages/website/ts/components/header.tsx b/packages/website/ts/components/header.tsx index 93a2b0e011..41bf8a3d2a 100644 --- a/packages/website/ts/components/header.tsx +++ b/packages/website/ts/components/header.tsx @@ -79,7 +79,12 @@ class HeaderBase extends React.Component { const { isNavToggled, toggleMobileNav, theme } = this.props; return ( - + @@ -94,7 +99,7 @@ class HeaderBase extends React.Component { - Explore 0x + Trade on 0x diff --git a/packages/website/ts/components/newLayout.tsx b/packages/website/ts/components/newLayout.tsx index ffc0e90758..ebe976d8ea 100644 --- a/packages/website/ts/components/newLayout.tsx +++ b/packages/website/ts/components/newLayout.tsx @@ -93,8 +93,7 @@ const SectionBase = styled.section` width: ${props => !props.isFullWidth && 'calc(100% - 60px)'}; max-width: 1500px; margin: 0 auto; - padding: ${props => props.isPadded && '120px 0'}; - padding: ${props => !!props.padding && props.padding}; + padding: ${props => (!!props.padding && props.padding) || (props.isPadded && '120px 0')}; background-color: ${props => props.theme[`${props.bgColor}BgColor`] || props.bgColor}; position: relative; overflow: ${props => !props.isFullWidth && 'hidden'}; diff --git a/packages/website/ts/components/portal/menu.tsx b/packages/website/ts/components/portal/menu.tsx index d591016869..fee347dd99 100644 --- a/packages/website/ts/components/portal/menu.tsx +++ b/packages/website/ts/components/portal/menu.tsx @@ -63,7 +63,7 @@ const DEFAULT_MENU_THEME: MenuTheme = { export const Menu: React.StatelessComponent = (props: MenuProps) => { return ( -
+
{_.map(props.menuItemEntries, entry => { const isSelected = entry.to === props.selectedPath; return ( diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 3d7c672583..4af0e883bd 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -2,9 +2,10 @@ import { colors, Link } from '@0x/react-shared'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; 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 { ANNOUNCEMENT_BANNER_HEIGHT, AnnouncementBanner } from 'ts/components/annoucement_banner'; import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_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 { AssetPicker } from 'ts/components/generate_order/asset_picker'; import { MetaTags } from 'ts/components/meta_tags'; -import { BackButton } from 'ts/components/portal/back_button'; import { Loading } from 'ts/components/portal/loading'; import { Menu, MenuTheme } from 'ts/components/portal/menu'; import { Section } from 'ts/components/portal/section'; @@ -88,6 +88,7 @@ interface PortalState { isLedgerDialogOpen: boolean; tokenManagementState: TokenManagementState; trackedTokenStateByAddress: TokenStateByAddress; + dismissBanner: boolean; } interface AccountManagementItem { @@ -133,6 +134,7 @@ export class Portal extends React.Component { isDisclaimerDialogOpen: !hasAcceptedDisclaimer, tokenManagementState: TokenManagementState.None, isLedgerDialogOpen: false, + dismissBanner: false, trackedTokenStateByAddress: initialTrackedTokenStateByAddress, }; } @@ -233,6 +235,18 @@ export class Portal extends React.Component { return ( + { style={{ backgroundColor: colors.lightestGrey, position: 'fixed', + transition: '300ms top ease-in-out', + top: this.state.dismissBanner ? '0' : ANNOUNCEMENT_BANNER_HEIGHT, zIndex: zIndex.topBar, }} maxWidth={LARGE_LAYOUT_MAX_WIDTH} /> - + - + { ); } + private _dismissBanner(): void { + this.setState({dismissBanner: true}); + } + + // tslint:disable-next-line:no-unused-variable private _renderMainRoute(): React.ReactNode { if (this._isSmallScreen()) { return ; @@ -317,12 +340,7 @@ export class Portal extends React.Component { selectedIconColor: colors.yellow800, selectedBackgroundColor: 'transparent', }; - return ( -
} - body={} - /> - ); + return
} />; } private _renderWallet(): React.ReactNode { const isMobile = utils.isMobileWidth(this.props.screenWidth); diff --git a/packages/website/ts/components/portal/section.tsx b/packages/website/ts/components/portal/section.tsx index b6c9fd0986..3032e44e42 100644 --- a/packages/website/ts/components/portal/section.tsx +++ b/packages/website/ts/components/portal/section.tsx @@ -1,13 +1,13 @@ import * as React from 'react'; export interface SectionProps { - header: React.ReactNode; + header?: React.ReactNode; body: React.ReactNode; } export const Section = (props: SectionProps) => { return (
- {props.header} + {!!props.header && props.header} {props.body}
); diff --git a/packages/website/ts/components/ui/search_textfield.tsx b/packages/website/ts/components/ui/search_textfield.tsx index ef94e17cd6..367d7d3ac7 100644 --- a/packages/website/ts/components/ui/search_textfield.tsx +++ b/packages/website/ts/components/ui/search_textfield.tsx @@ -7,22 +7,24 @@ interface InputProps { name?: string; width?: string; type?: string; + value?: string; defaultValue?: string; placeholder?: string; onChange?: (e: React.ChangeEvent) => void; } export const Input = React.forwardRef((props: InputProps, ref?: React.Ref) => { - 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 inputProps = { name, type }; return ( - + props.type === 'textarea' && `120px`}; diff --git a/packages/website/ts/icons/illustrations/search.svg b/packages/website/ts/icons/illustrations/search.svg index 040ba824e6..92f18ad017 100644 --- a/packages/website/ts/icons/illustrations/search.svg +++ b/packages/website/ts/icons/illustrations/search.svg @@ -1,4 +1,4 @@ - - + + diff --git a/packages/website/ts/pages/explore.tsx b/packages/website/ts/pages/explore.tsx index 0c48b4a033..63c9a35e19 100644 --- a/packages/website/ts/pages/explore.tsx +++ b/packages/website/ts/pages/explore.tsx @@ -14,7 +14,10 @@ import { BY_NAME_ORDERINGS, EDITORIAL, FILTERS, ORDERINGS, PROJECTS } from 'ts/p import { ExploreSettingsDropdown } from 'ts/pages/explore/explore_dropdown'; import { ExploreGrid } from 'ts/pages/explore/explore_grid'; 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 { ExploreAnalyticAction, ExploreFilterMetadata, @@ -43,34 +46,36 @@ export class Explore extends React.Component { isTilesLoading: false, isContactModalOpen: false, tiles: [] as ExploreTile[], - tilesOrdering: ExploreTilesOrdering.None, + tilesOrdering: ExploreTilesOrdering.Popular, isEditorialShown: true, filters: FILTERS, query: '', }; + private readonly _debouncedChangeSearchResults: (query: string) => void; + constructor(props: ExploreProps) { super(props); + this._debouncedChangeSearchResults = _.debounce(this._changeSearchResults, 300); } - public componentWillMount(): void { - // tslint:disable-next-line:no-floating-promises - this._loadEntriesAsync().then(() => { - this._setFilter('all'); - }); + // tslint:disable-next-line:async-suffix + public async componentDidMount(): Promise { + await this._loadEntriesAsync(); + await this._setFilter('all'); } public render(): React.ReactNode { return ( - -
+ +
{ heading="Working on a 0x project?" subline="Lorem Ipsum something then that and say something more." 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 }} /> { this.setState({ isContactModalOpen: false }); }; - private readonly _onEditorial = (newValue: boolean): void => { - // tslint:disable-next-line:no-floating-promises - this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Editorial, { + // tslint:disable-next-line:no-unused-variable + private readonly _onEditorial = async (newValue: boolean): Promise => { + const newTiles = await this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Editorial, { 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 => { this.setState({ tilesOrdering: newValue }); // 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, - }).then(newTiles => { - this.setState({ tilesOrdering: newValue, tiles: newTiles }); }); + this.setState({ tilesOrdering: newValue, tiles: newTiles }); }; private readonly _launchInstantAsync = (params: ExploreProjectInstantMetadata): void => { @@ -125,6 +128,16 @@ export class Explore extends React.Component { private readonly _onAnalytics = (project: ExploreProject, action: ExploreAnalyticAction): void => { // 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 { @@ -155,24 +168,24 @@ export class Explore extends React.Component { return this.state.tiles; }; - private readonly _changeSearchResults = (query: string): void => { - const trimmedQuery = query.trim().toLowerCase(); - // tslint:disable-next-line:no-floating-promises - 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 _setNewQuery = (query: string): void => { + this.setState({ query }); + this._debouncedChangeSearchResults(query); }; - private readonly _setFilter = (filterName: string, active: boolean = true): void => { + private readonly _changeSearchResults = async (query: string): Promise => { + // 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 => { let updatedFilters: ExploreFilterMetadata[]; updatedFilters = this.state.filters.map(f => { const newFilter = _.assign({}, f); @@ -181,14 +194,18 @@ export class Explore extends React.Component { }); // If no filters are enabled, default to all if (_.filter(updatedFilters, f => f.active).length === 0) { - this._setFilter('all'); + await this._setFilter('all'); } else { // tslint:disable-next-line:no-floating-promises - this._generateTilesWithModifier(this.state.tiles, ExploreTilesModifiers.Filter, { - filter: updatedFilters.find(f => f.active), - }).then(newTiles => { - this.setState({ filters: updatedFilters, tiles: newTiles }); - }); + const newTiles = await this._generateTilesWithModifier( + this.state.tiles, + _.isEmpty(this.state.query) ? ExploreTilesModifiers.Filter : ExploreTilesModifiers.Search, + { + 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 { modifier: ExploreTilesModifiers, options: ExploreModifierOptions, ): Promise => { + const trimmedQuery = modifier === ExploreTilesModifiers.Search ? options.query.trim().toLowerCase() : ''; if (!this._verifyExploreTilesModifierOptions(modifier, options)) { return tiles; } @@ -244,7 +262,7 @@ export class Explore extends React.Component { newTile.visibility === ExploreTileVisibility.Visible ) { newTile.visibility = - (_.startsWith(newTile.exploreProject.label.toLowerCase(), options.query) && + (_.startsWith(newTile.exploreProject.label.toLowerCase(), trimmedQuery) && ExploreTileVisibility.Visible) || ExploreTileVisibility.Hidden; } @@ -280,7 +298,7 @@ export class Explore extends React.Component { tilesOrdering: this.state.tilesOrdering, }); this.setState({ tiles: orderedTiles, isEntriesLoading: false }, () => { - this._generateEditorialContent(); + // this._generateEditorialContent(); }); }; } @@ -292,22 +310,21 @@ const ExploreHeroContentWrapper = styled.div` `; interface ExploreHeroProps { + query: string; onSearch(query: string): void; } const ExploreHero = (props: ExploreHeroProps) => { - // tslint:disable-next-line:no-unbound-method - const onSearchDebounce = _.debounce(props.onSearch, 300); const onChange = (e: any) => { - onSearchDebounce(e.target.value); + props.onSearch(e.target.value); }; return ( -
+
Explore 0x - +
); @@ -320,7 +337,7 @@ const ExploreToolBarWrapper = styled.div` const ExploreToolBarContentWrapper = styled.div` display: flex; - padding: 2rem 0; + padding-bottom: 2rem; & > * { margin: 0 0.3rem; } @@ -336,9 +353,9 @@ interface ExploreToolBarProps { filters: ExploreFilterMetadata[]; activeOrdering: ExploreTilesOrdering; orderings: ExploreTilesOrderingMetadata[]; - editorial: boolean; + editorial?: boolean; onOrdering: (newValue: string) => void; - onEditorial: (newValue: boolean) => void; + onEditorial?: (newValue: boolean) => void; onFilterClick(filterName: string, active: boolean): void; } @@ -358,9 +375,7 @@ const ExploreToolBar = (props: ExploreToolBarProps) => { ); })} - - - + ); }; diff --git a/packages/website/ts/pages/explore/explore_content.tsx b/packages/website/ts/pages/explore/explore_content.tsx index 4dd6108fa8..f7c5a9b117 100644 --- a/packages/website/ts/pages/explore/explore_content.tsx +++ b/packages/website/ts/pages/explore/explore_content.tsx @@ -99,23 +99,30 @@ export const FILTERS: ExploreFilterMetadata[] = [ label: 'Relayer', name: 'relayer', filterType: ExploreFilterType.Keyword, - keyword: 'relayer', }, { label: 'Collectibles', name: 'collectibles', filterType: ExploreFilterType.Keyword, - keyword: 'ERC-721', }, ]; 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: 'Recently Added', + ordering: ExploreTilesOrdering.RecentlyAdded, + type: ExploreTilesOrderingType.HardCodedByName, + }, + { + label: 'Alphabetical', + ordering: ExploreTilesOrdering.Alphabetical, + type: ExploreTilesOrderingType.HardCodedByName, + }, ]; export const BY_NAME_ORDERINGS: { [s: string]: string[] } = { [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'], }; diff --git a/packages/website/ts/pages/explore/explore_dropdown.tsx b/packages/website/ts/pages/explore/explore_dropdown.tsx index d974ff0178..a3470ea774 100644 --- a/packages/website/ts/pages/explore/explore_dropdown.tsx +++ b/packages/website/ts/pages/explore/explore_dropdown.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components'; import { Icon } from 'ts/components/icon'; import { Heading } from 'ts/components/text'; 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 { ExploreTilesOrdering, ExploreTilesOrderingMetadata } from 'ts/types'; @@ -32,7 +32,7 @@ const ExploreSettingsDropdownButton = ({}) => { - Settings + Sort ); }; @@ -65,7 +65,7 @@ interface DropdownWrapInterface { const DropdownWrap = styled.div` width: ${props => props.width || 280}px; - margin-top: 16px; + margin-top: calc(16px - 2rem); padding: 16px 24px; border: 1px solid transparent; border-color: ${props => props.theme.dropdownBorderColor}; @@ -107,27 +107,29 @@ const DropdownWrap = styled.div` @media (max-width: 768px) { 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 { activeOrdering: ExploreTilesOrdering; orderings: ExploreTilesOrderingMetadata[]; onOrdering: (newValue: string) => void; - onEditorial: (newValue: boolean) => void; - editorial: boolean; + onEditorial?: (newValue: boolean) => void; + editorial?: boolean; } export class ExploreSettingsDropdown extends React.Component { - constructor(props: ExploreSettingsDropdownProps) { - super(props); - } - public render(): React.ReactNode { return ( + {!!this.props.onEditorial &&
Editorial content reflects the views of the 0x core team. -
+
} - + {/* Ordering - + */} {this.props.orderings.map(o => { const onClick = () => this.props.onOrdering(o.ordering); @@ -167,19 +169,18 @@ export class ExploreSettingsDropdown extends React.Component props.theme.dropdownColor}; - opacity: 0.15; - position: absolute; - top: 0; - left: 0; - } + // padding-top: 20px; + // margin-bottom: 20px; + // &:before { + // content: ''; + // width: 100%; + // height: 1px; + // background-color: ${props => props.theme.dropdownColor}; + // opacity: 0.15; + // position: absolute; + // top: 0; + // left: 0; + // } `; diff --git a/packages/website/ts/pages/explore/explore_grid.tsx b/packages/website/ts/pages/explore/explore_grid.tsx index 4ff12cd003..2ce1268747 100644 --- a/packages/website/ts/pages/explore/explore_grid.tsx +++ b/packages/website/ts/pages/explore/explore_grid.tsx @@ -15,10 +15,6 @@ interface RicherExploreGridListTile extends ExploreTile { } export class ExploreGrid extends React.Component { - constructor(props: ExptoreGridProps) { - super(props); - } - public render(): React.ReactNode { return ( @@ -79,6 +75,9 @@ const ExploreGridList = styled.div` grid-template-columns: repeat(${ExploreTileWidth.FullWidth}, 1fr); grid-column-gap: 1.5rem; grid-row-gap: 1.5rem; + & > * { + align-self: stretch; + } @media (max-width: 56rem) { grid-template-columns: repeat(2, 1fr); } diff --git a/packages/website/ts/pages/explore/explore_grid_tile.tsx b/packages/website/ts/pages/explore/explore_grid_tile.tsx index 70ef4cb28b..0b1f42aa98 100644 --- a/packages/website/ts/pages/explore/explore_grid_tile.tsx +++ b/packages/website/ts/pages/explore/explore_grid_tile.tsx @@ -1,3 +1,4 @@ +import * as _ from 'lodash'; import * as React from 'react'; import styled from 'styled-components'; @@ -6,11 +7,9 @@ import { Heading, Paragraph } from 'ts/components/text'; import { Image } from 'ts/components/ui/image'; import { ExploreAnalyticAction, ExploreProject } from 'ts/types'; -const EMPTY_FUNCTION = () => true; - export const ExploreGridTile = (props: ExploreProject) => { // tslint:disable-next-line:no-unbound-method - const onAnalytics = props.onAnalytics || EMPTY_FUNCTION; + const onAnalytics = props.onAnalytics || _.noop; const onInstantClick = !!props.instant ? () => { props.onInstantClick(); @@ -21,9 +20,9 @@ export const ExploreGridTile = (props: ExploreProject) => { onAnalytics(ExploreAnalyticAction.LinkClick); }; return ( - + {!!onInstantClick && ( - + @@ -59,21 +58,32 @@ const ExploreGridHeroWell = styled.div` const ExploreGridContentWell = styled.div` padding: 1.5rem; + border: 1px solid rgba(0, 0, 0, 0.15); + border-top: 0; + flex-grow: 1; `; const ExploreGridTileLink = styled.a` - display: block; + display: flex; + flex-direction: column; + height: 100%; `; export const ExploreGridTileWrapper = styled.div` display: block; position: relative; background-color: white; + height: 100%; border: 1px solid rgba(0, 0, 0, 0.15); - // transition: box-shadow 200ms ease-in-out; - // &:hover { - // box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.1); - // } + & .explore-grid-instant-button-wrapper { + transition: opacity 0.2s, visibility 0.2s 0s; + opacity: 0; + visibility: hidden; + } + &:hover .explore-grid-instant-button-wrapper { + opacity: 1; + visibility: visible; + } `; const ExploreGridButtonWrapper = styled.div` diff --git a/packages/website/ts/pages/explore/explore_tag_button.tsx b/packages/website/ts/pages/explore/explore_tag_button.tsx index f1607aa3c9..a39a296427 100644 --- a/packages/website/ts/pages/explore/explore_tag_button.tsx +++ b/packages/website/ts/pages/explore/explore_tag_button.tsx @@ -17,7 +17,7 @@ export interface ButtonInterface { theme?: ThemeInterface; } -export const Button: React.StatelessComponent = (props: ButtonInterface) => { +export const ExploreTagButton: React.StatelessComponent = (props: ButtonInterface) => { const { children, isDisabled, className } = props; const buttonProps = { disabled: isDisabled }; @@ -29,7 +29,7 @@ export const Button: React.StatelessComponent = (props: ButtonI ); }; -Button.defaultProps = {}; +ExploreTagButton.defaultProps = {}; const ButtonBase = styled.button` appearance: none; diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 458db64c67..f900150717 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -265,8 +265,8 @@ export enum ExploreAnalyticAction { } export enum ExploreTilesOrdering { - None = 'NONE', - Latest = 'LATEST', + Alphabetical = 'ALPHABETICAL', + RecentlyAdded = 'RECENTLY_ADDED', Popular = 'POPULAR', } @@ -297,7 +297,6 @@ export interface ExploreFilterMetadata { label: string; filterType: ExploreFilterType; name: string; - keyword?: string; active?: boolean; }