aboutsummaryrefslogtreecommitdiffstats
path: root/packages/react-shared
diff options
context:
space:
mode:
Diffstat (limited to 'packages/react-shared')
-rw-r--r--packages/react-shared/package.json5
-rw-r--r--packages/react-shared/src/components/link.tsx101
-rw-r--r--packages/react-shared/src/components/nested_sidebar_menu.tsx95
-rw-r--r--packages/react-shared/src/index.ts3
-rw-r--r--packages/react-shared/src/types.ts17
-rw-r--r--packages/react-shared/src/utils/colors.ts7
6 files changed, 182 insertions, 46 deletions
diff --git a/packages/react-shared/package.json b/packages/react-shared/package.json
index 95bf849ad..5629ec915 100644
--- a/packages/react-shared/package.json
+++ b/packages/react-shared/package.json
@@ -34,6 +34,7 @@
"typescript": "3.0.1"
},
"dependencies": {
+ "@0xproject/types": "^1.1.2",
"@material-ui/core": "^3.0.1",
"@types/is-mobile": "0.3.0",
"@types/lodash": "4.14.104",
@@ -41,6 +42,7 @@
"@types/node": "*",
"@types/react": "*",
"@types/react-dom": "*",
+ "@types/react-router-dom": "^4.0.4",
"@types/react-scroll": "1.5.3",
"basscss": "^8.0.3",
"change-case": "^3.0.2",
@@ -51,7 +53,8 @@
"react-dom": "^16.4.2",
"react-highlight": "0xproject/react-highlight#2f40a42e0a3f0ad126f9f42d505b97b603fc7162",
"react-markdown": "^3.2.2",
- "react-scroll": "0xproject/react-scroll#similar-to-pr-330"
+ "react-scroll": "0xproject/react-scroll#similar-to-pr-330",
+ "react-router-dom": "^4.1.1"
},
"publishConfig": {
"access": "public"
diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx
new file mode 100644
index 000000000..6200bfbd3
--- /dev/null
+++ b/packages/react-shared/src/components/link.tsx
@@ -0,0 +1,101 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import { Link as ReactRounterLink } from 'react-router-dom';
+import { Link as ScrollLink } from 'react-scroll';
+
+import { LinkType } from '../types';
+import { constants } from '../utils/constants';
+
+export interface LinkProps {
+ to: string;
+ type?: LinkType;
+ shouldOpenInNewTab?: boolean;
+ style?: React.CSSProperties;
+ className?: string;
+ onMouseOver?: (event: React.MouseEvent<HTMLElement>) => void;
+ onMouseLeave?: (event: React.MouseEvent<HTMLElement>) => void;
+ onMouseEnter?: (event: React.MouseEvent<HTMLElement>) => void;
+ containerId?: string;
+}
+
+/**
+ * A generic link component which let's the developer render internal, external and scroll-to-hash links, and
+ * their associated behaviors with a single link component. Many times we want a menu including a combination of
+ * internal, external and scroll links and the abstraction of the differences of rendering each types of link
+ * makes it much easier to do so.
+ */
+export const Link: React.StatelessComponent<LinkProps> = ({
+ style,
+ className,
+ type,
+ to,
+ shouldOpenInNewTab,
+ children,
+ onMouseOver,
+ onMouseLeave,
+ onMouseEnter,
+ containerId,
+}) => {
+ const styleWithDefault = {
+ textDecoration: 'none',
+ ...style,
+ };
+
+ switch (type) {
+ case LinkType.External:
+ return (
+ <a
+ target={shouldOpenInNewTab ? '_blank' : ''}
+ className={className}
+ style={styleWithDefault}
+ href={to}
+ onMouseOver={onMouseOver}
+ onMouseEnter={onMouseEnter}
+ onMouseLeave={onMouseLeave}
+ >
+ {children}
+ </a>
+ );
+ case LinkType.ReactRoute:
+ return (
+ <ReactRounterLink
+ to={to}
+ className={className}
+ style={styleWithDefault}
+ target={shouldOpenInNewTab ? '_blank' : ''}
+ onMouseOver={onMouseOver}
+ onMouseEnter={onMouseEnter}
+ onMouseLeave={onMouseLeave}
+ >
+ {children}
+ </ReactRounterLink>
+ );
+ case LinkType.ReactScroll:
+ return (
+ <ScrollLink
+ to={to}
+ offset={0}
+ hashSpy={true}
+ duration={constants.DOCS_SCROLL_DURATION_MS}
+ containerId={containerId}
+ >
+ {children}
+ </ScrollLink>
+ );
+ default:
+ throw new Error(`Unrecognized LinkType: ${type}`);
+ }
+};
+
+Link.defaultProps = {
+ type: LinkType.ReactRoute,
+ shouldOpenInNewTab: false,
+ style: {},
+ className: '',
+ onMouseOver: _.noop.bind(_),
+ onMouseLeave: _.noop.bind(_),
+ onMouseEnter: _.noop.bind(_),
+ containerId: constants.DOCS_CONTAINER_ID,
+};
+
+Link.displayName = 'Link';
diff --git a/packages/react-shared/src/components/nested_sidebar_menu.tsx b/packages/react-shared/src/components/nested_sidebar_menu.tsx
index c8bddb59a..0e6a06a3b 100644
--- a/packages/react-shared/src/components/nested_sidebar_menu.tsx
+++ b/packages/react-shared/src/components/nested_sidebar_menu.tsx
@@ -1,24 +1,26 @@
+import { ObjectMap } from '@0xproject/types';
import * as _ from 'lodash';
import MenuItem from 'material-ui/MenuItem';
import * as React from 'react';
-import { Link as ScrollLink } from 'react-scroll';
-import { MenuSubsectionsBySection, Styles } from '../types';
+import { ALink, Styles } from '../types';
import { colors } from '../utils/colors';
import { constants } from '../utils/constants';
import { utils } from '../utils/utils';
+import { Link } from './Link';
import { VersionDropDown } from './version_drop_down';
export interface NestedSidebarMenuProps {
- topLevelMenu: { [topLevel: string]: string[] };
- menuSubsectionsBySection: MenuSubsectionsBySection;
+ sectionNameToLinks: ObjectMap<ALink[]>;
+ subsectionNameToLinks?: ObjectMap<ALink[]>;
sidebarHeader?: React.ReactNode;
shouldDisplaySectionHeaders?: boolean;
onMenuItemClick?: () => void;
selectedVersion?: string;
versions?: string[];
onVersionSelected?: (semver: string) => void;
+ shouldReformatMenuItemNames?: boolean;
}
export interface NestedSidebarMenuState {}
@@ -42,23 +44,24 @@ export class NestedSidebarMenu extends React.Component<NestedSidebarMenuProps, N
public static defaultProps: Partial<NestedSidebarMenuProps> = {
shouldDisplaySectionHeaders: true,
onMenuItemClick: _.noop.bind(_),
+ shouldReformatMenuItemNames: true,
+ subsectionNameToLinks: {},
};
public render(): React.ReactNode {
- const navigation = _.map(this.props.topLevelMenu, (menuItems: string[], sectionName: string) => {
+ const navigation = _.map(this.props.sectionNameToLinks, (links: ALink[], sectionName: string) => {
const finalSectionName = utils.convertCamelCaseToSpaces(sectionName);
if (this.props.shouldDisplaySectionHeaders) {
// tslint:disable-next-line:no-unused-variable
- const id = utils.getIdFromName(sectionName);
return (
- <div key={`section-${sectionName}`} className="py1" style={{ color: colors.grey800 }}>
- <div style={{ fontWeight: 'bold', fontSize: 15 }} className="py1">
+ <div key={`section-${sectionName}`} className="py1" style={{ color: colors.linkSectionGrey }}>
+ <div style={{ fontWeight: 'bold', fontSize: 15, letterSpacing: 0.5 }} className="py1">
{finalSectionName.toUpperCase()}
</div>
- {this._renderMenuItems(menuItems)}
+ {this._renderMenuItems(links)}
</div>
);
} else {
- return <div key={`section-${sectionName}`}>{this._renderMenuItems(menuItems)}</div>;
+ return <div key={`section-${sectionName}`}>{this._renderMenuItems(links)}</div>;
}
});
const maxWidthWithScrollbar = 307;
@@ -80,53 +83,63 @@ export class NestedSidebarMenu extends React.Component<NestedSidebarMenuProps, N
</div>
);
}
- private _renderMenuItems(menuItemNames: string[]): React.ReactNode[] {
+ private _renderMenuItems(links: ALink[]): React.ReactNode[] {
const menuItemStyles = this.props.shouldDisplaySectionHeaders
? styles.menuItemWithHeaders
: styles.menuItemWithoutHeaders;
const menuItemInnerDivStyles = this.props.shouldDisplaySectionHeaders ? styles.menuItemInnerDivWithHeaders : {};
- const menuItems = _.map(menuItemNames, menuItemName => {
- const finalMenuItemName = utils.convertDashesToSpaces(menuItemName);
- const id = utils.getIdFromName(menuItemName);
+ const menuItems = _.map(links, link => {
+ const finalMenuItemName = this.props.shouldReformatMenuItemNames
+ ? utils.convertDashesToSpaces(link.title)
+ : link.title;
return (
- <div key={menuItemName}>
- <ScrollLink
- key={`menuItem-${menuItemName}`}
- to={id}
- offset={0}
- hashSpy={true}
- duration={constants.DOCS_SCROLL_DURATION_MS}
+ <div key={`menuItem-${finalMenuItemName}`}>
+ <Link
+ to={link.to}
+ type={link.type}
+ shouldOpenInNewTab={link.shouldOpenInNewTab}
containerId={constants.DOCS_CONTAINER_ID}
>
- <MenuItem style={menuItemStyles} innerDivStyle={menuItemInnerDivStyles}>
- <span style={{ textTransform: 'capitalize' }}>{finalMenuItemName}</span>
+ <MenuItem
+ style={menuItemStyles}
+ innerDivStyle={menuItemInnerDivStyles}
+ onClick={this._onMenuItemClick.bind(this)}
+ >
+ <span
+ style={{
+ textTransform: this.props.shouldReformatMenuItemNames ? 'capitalize' : 'none',
+ }}
+ >
+ {finalMenuItemName}
+ </span>
</MenuItem>
- </ScrollLink>
- {this._renderMenuItemSubsections(menuItemName)}
+ </Link>
+ {this._renderMenuItemSubsections(link.title)}
</div>
);
});
return menuItems;
}
private _renderMenuItemSubsections(menuItemName: string): React.ReactNode {
- if (_.isUndefined(this.props.menuSubsectionsBySection[menuItemName])) {
+ if (
+ _.isUndefined(this.props.subsectionNameToLinks) ||
+ _.isUndefined(this.props.subsectionNameToLinks[menuItemName])
+ ) {
return null;
}
- return this._renderMenuSubsectionsBySection(menuItemName, this.props.menuSubsectionsBySection[menuItemName]);
+ return this._renderSubsectionLinks(menuItemName, this.props.subsectionNameToLinks[menuItemName]);
}
- private _renderMenuSubsectionsBySection(menuItemName: string, entityNames: string[]): React.ReactNode {
+ private _renderSubsectionLinks(menuItemName: string, links: ALink[]): React.ReactNode {
return (
<ul style={{ margin: 0, listStyleType: 'none', paddingLeft: 0 }} key={menuItemName}>
- {_.map(entityNames, entityName => {
- const name = `${menuItemName}-${entityName}`;
- const id = utils.getIdFromName(name);
+ {_.map(links, link => {
+ const name = `${menuItemName}-${link.title}`;
return (
<li key={`menuSubsectionItem-${name}`}>
- <ScrollLink
- to={id}
- offset={0}
- hashSpy={true}
- duration={constants.DOCS_SCROLL_DURATION_MS}
+ <Link
+ to={link.to}
+ type={link.type}
+ shouldOpenInNewTab={link.shouldOpenInNewTab}
containerId={constants.DOCS_CONTAINER_ID}
>
<MenuItem
@@ -136,14 +149,20 @@ export class NestedSidebarMenu extends React.Component<NestedSidebarMenuProps, N
fontSize: 14,
lineHeight: '35px',
}}
+ onClick={this._onMenuItemClick.bind(this)}
>
- {entityName}
+ {link.title}
</MenuItem>
- </ScrollLink>
+ </Link>
</li>
);
})}
</ul>
);
}
+ private _onMenuItemClick(): void {
+ if (!_.isUndefined(this.props.onMenuItemClick)) {
+ this.props.onMenuItemClick();
+ }
+ }
}
diff --git a/packages/react-shared/src/index.ts b/packages/react-shared/src/index.ts
index 3b50c0117..1842e8af3 100644
--- a/packages/react-shared/src/index.ts
+++ b/packages/react-shared/src/index.ts
@@ -4,8 +4,9 @@ export { MarkdownCodeBlock } from './components/markdown_code_block';
export { MarkdownSection } from './components/markdown_section';
export { NestedSidebarMenu } from './components/nested_sidebar_menu';
export { SectionHeader } from './components/section_header';
+export { Link } from './components/link';
-export { HeaderSizes, Styles, MenuSubsectionsBySection, EtherscanLinkSuffixes, Networks } from './types';
+export { HeaderSizes, Styles, EtherscanLinkSuffixes, Networks, LinkType, ALink } from './types';
export { utils } from './utils/utils';
export { constants } from './utils/constants';
diff --git a/packages/react-shared/src/types.ts b/packages/react-shared/src/types.ts
index 88fadcc09..daae7b115 100644
--- a/packages/react-shared/src/types.ts
+++ b/packages/react-shared/src/types.ts
@@ -8,10 +8,6 @@ export enum HeaderSizes {
H3 = 'h3',
}
-export interface MenuSubsectionsBySection {
- [section: string]: string[];
-}
-
export enum EtherscanLinkSuffixes {
Address = 'address',
Tx = 'tx',
@@ -23,3 +19,16 @@ export enum Networks {
Ropsten = 'Ropsten',
Rinkeby = 'Rinkeby',
}
+
+export enum LinkType {
+ External = 'EXTERNAL',
+ ReactScroll = 'REACT_SCROLL',
+ ReactRoute = 'REACT_ROUTE',
+}
+
+export interface ALink {
+ title: string;
+ to: string;
+ shouldOpenInNewTab?: boolean;
+ type?: LinkType;
+}
diff --git a/packages/react-shared/src/utils/colors.ts b/packages/react-shared/src/utils/colors.ts
index 5a20eeaa1..596c17e83 100644
--- a/packages/react-shared/src/utils/colors.ts
+++ b/packages/react-shared/src/utils/colors.ts
@@ -9,7 +9,8 @@ const baseColors = {
grey300: '#E0E0E0',
beigeWhite: '#E4E4E4',
lightBgGrey: '#EDEDED',
- grey350: '#cacaca',
+ grey325: '#dfdfdf',
+ grey350: '#CACACA',
grey400: '#BDBDBD',
lightGrey: '#BBBBBB',
grey500: '#9E9E9E',
@@ -24,10 +25,12 @@ const baseColors = {
heroGrey: '#404040',
projectsGrey: '#343333',
darkestGrey: '#272727',
+ lightestBlue: '#E7F1FD',
lightBlue: '#60A4F4',
lightBlueA700: '#0091EA',
- linkBlue: '#1D5CDE',
+ lightLinkBlue: '#3289F1',
mediumBlue: '#488AEA',
+ linkBlue: '#1D5CDE',
darkBlue: '#4D5481',
lightTurquois: '#aefcdc',
turquois: '#058789',