From ca25b816fabe15ce1ebc539c0316beba813683b8 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 13 Mar 2018 15:29:12 +0100 Subject: move scripts to monorepro-scripts --- .../react-shared/src/components/anchor_title.tsx | 87 ++++++++++++ .../src/components/markdown_code_block.tsx | 25 ++++ .../src/components/markdown_link_block.tsx | 47 ++++++ .../src/components/markdown_section.tsx | 94 ++++++++++++ .../src/components/nested_sidebar_menu.tsx | 158 +++++++++++++++++++++ .../react-shared/src/components/section_header.tsx | 73 ++++++++++ .../src/components/version_drop_down.tsx | 39 +++++ 7 files changed, 523 insertions(+) create mode 100644 packages/react-shared/src/components/anchor_title.tsx create mode 100644 packages/react-shared/src/components/markdown_code_block.tsx create mode 100644 packages/react-shared/src/components/markdown_link_block.tsx create mode 100644 packages/react-shared/src/components/markdown_section.tsx create mode 100644 packages/react-shared/src/components/nested_sidebar_menu.tsx create mode 100644 packages/react-shared/src/components/section_header.tsx create mode 100644 packages/react-shared/src/components/version_drop_down.tsx (limited to 'packages/react-shared/src/components') diff --git a/packages/react-shared/src/components/anchor_title.tsx b/packages/react-shared/src/components/anchor_title.tsx new file mode 100644 index 000000000..f44354097 --- /dev/null +++ b/packages/react-shared/src/components/anchor_title.tsx @@ -0,0 +1,87 @@ +import * as React from 'react'; +import { Link as ScrollLink } from 'react-scroll'; + +import { HeaderSizes, Styles } from '../types'; +import { constants } from '../utils/constants'; +import { utils } from '../utils/utils'; + +const headerSizeToScrollOffset: { [headerSize: string]: number } = { + h2: -20, + h3: 0, +}; + +export interface AnchorTitleProps { + title: string | React.ReactNode; + id: string; + headerSize: HeaderSizes; + shouldShowAnchor: boolean; +} + +export interface AnchorTitleState { + isHovering: boolean; +} + +const styles: Styles = { + anchor: { + fontSize: 20, + transform: 'rotate(45deg)', + cursor: 'pointer', + }, + headers: { + WebkitMarginStart: 0, + WebkitMarginEnd: 0, + fontWeight: 'bold', + display: 'block', + }, + h1: { + fontSize: '1.8em', + }, + h2: { + fontSize: '1.5em', + fontWeight: 400, + }, + h3: { + fontSize: '1.17em', + }, +}; + +export class AnchorTitle extends React.Component { + constructor(props: AnchorTitleProps) { + super(props); + this.state = { + isHovering: false, + }; + } + public render() { + let opacity = 0; + if (this.props.shouldShowAnchor) { + opacity = this.state.isHovering ? 0.6 : 1; + } + return ( +
+
+ {this.props.title} +
+ + + +
+ ); + } + private _setHoverState(isHovering: boolean) { + this.setState({ + isHovering, + }); + } +} diff --git a/packages/react-shared/src/components/markdown_code_block.tsx b/packages/react-shared/src/components/markdown_code_block.tsx new file mode 100644 index 000000000..2070bb8e1 --- /dev/null +++ b/packages/react-shared/src/components/markdown_code_block.tsx @@ -0,0 +1,25 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import * as HighLight from 'react-highlight'; + +export interface MarkdownCodeBlockProps { + value: string; + language: string; +} + +export interface MarkdownCodeBlockState {} + +export class MarkdownCodeBlock extends React.Component { + // Re-rendering a codeblock causes any use selection to become de-selected. This is annoying when trying + // to copy-paste code examples. We therefore noop re-renders on this component if it's props haven't changed. + public shouldComponentUpdate(nextProps: MarkdownCodeBlockProps, nextState: MarkdownCodeBlockState) { + return nextProps.value !== this.props.value || nextProps.language !== this.props.language; + } + public render() { + return ( + + {this.props.value} + + ); + } +} diff --git a/packages/react-shared/src/components/markdown_link_block.tsx b/packages/react-shared/src/components/markdown_link_block.tsx new file mode 100644 index 000000000..8f5862249 --- /dev/null +++ b/packages/react-shared/src/components/markdown_link_block.tsx @@ -0,0 +1,47 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { constants } from '../utils/constants'; +import { utils } from '../utils/utils'; + +export interface MarkdownLinkBlockProps { + href: string; +} + +export interface MarkdownLinkBlockState {} + +export class MarkdownLinkBlock extends React.Component { + // Re-rendering a linkBlock causes it to remain unclickable. + // We therefore noop re-renders on this component if it's props haven't changed. + public shouldComponentUpdate(nextProps: MarkdownLinkBlockProps, nextState: MarkdownLinkBlockState) { + return nextProps.href !== this.props.href; + } + public render() { + const href = this.props.href; + const isLinkToSection = _.startsWith(href, '#'); + // If protocol is http or https, we can open in a new tab, otherwise don't for security reasons + if (_.startsWith(href, 'http') || _.startsWith(href, 'https')) { + return ( + + {this.props.children} + + ); + } else if (isLinkToSection) { + return ( + + {this.props.children} + + ); + } else { + return {this.props.children}; + } + } + private _onHashUrlClick(href: string) { + const hash = href.split('#')[1]; + utils.scrollToHash(hash, constants.SCROLL_CONTAINER_ID); + utils.setUrlHash(hash); + } +} diff --git a/packages/react-shared/src/components/markdown_section.tsx b/packages/react-shared/src/components/markdown_section.tsx new file mode 100644 index 000000000..d24a43dcb --- /dev/null +++ b/packages/react-shared/src/components/markdown_section.tsx @@ -0,0 +1,94 @@ +import * as _ from 'lodash'; +import RaisedButton from 'material-ui/RaisedButton'; +import * as React from 'react'; +import * as ReactMarkdown from 'react-markdown'; +import { Element as ScrollElement } from 'react-scroll'; + +import { HeaderSizes } from '../types'; +import { colors } from '../utils/colors'; +import { utils } from '../utils/utils'; + +import { AnchorTitle } from './anchor_title'; +import { MarkdownCodeBlock } from './markdown_code_block'; +import { MarkdownLinkBlock } from './markdown_link_block'; + +export interface MarkdownSectionProps { + sectionName: string; + markdownContent: string; + headerSize?: HeaderSizes; + githubLink?: string; +} + +interface DefaultMarkdownSectionProps { + headerSize: HeaderSizes; +} + +type PropsWithDefaults = MarkdownSectionProps & DefaultMarkdownSectionProps; + +export interface MarkdownSectionState { + shouldShowAnchor: boolean; +} + +export class MarkdownSection extends React.Component { + public static defaultProps: Partial = { + headerSize: HeaderSizes.H3, + }; + constructor(props: MarkdownSectionProps) { + super(props); + this.state = { + shouldShowAnchor: false, + }; + } + public render() { + const { sectionName, markdownContent, headerSize, githubLink } = this.props as PropsWithDefaults; + + const id = utils.getIdFromName(sectionName); + return ( +
+ +
+
+ + + +
+
+ {!_.isUndefined(githubLink) && ( + + Edit on Github + + )} +
+
+
+ +
+
+ ); + } + private _setAnchorVisibility(shouldShowAnchor: boolean) { + this.setState({ + shouldShowAnchor, + }); + } +} diff --git a/packages/react-shared/src/components/nested_sidebar_menu.tsx b/packages/react-shared/src/components/nested_sidebar_menu.tsx new file mode 100644 index 000000000..2225bd197 --- /dev/null +++ b/packages/react-shared/src/components/nested_sidebar_menu.tsx @@ -0,0 +1,158 @@ +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 { colors } from '../utils/colors'; +import { constants } from '../utils/constants'; +import { utils } from '../utils/utils'; + +import { VersionDropDown } from './version_drop_down'; + +export interface NestedSidebarMenuProps { + topLevelMenu: { [topLevel: string]: string[] }; + menuSubsectionsBySection: MenuSubsectionsBySection; + sidebarHeader?: React.ReactNode; + shouldDisplaySectionHeaders?: boolean; + onMenuItemClick?: () => void; + selectedVersion?: string; + versions?: string[]; + onVersionSelected?: (semver: string) => void; +} + +export interface NestedSidebarMenuState {} + +const styles: Styles = { + menuItemWithHeaders: { + minHeight: 0, + }, + menuItemWithoutHeaders: { + minHeight: 48, + }, + menuItemInnerDivWithHeaders: { + color: colors.grey800, + fontSize: 14, + lineHeight: 2, + padding: 0, + }, +}; + +export class NestedSidebarMenu extends React.Component { + public static defaultProps: Partial = { + shouldDisplaySectionHeaders: true, + onMenuItemClick: _.noop, + }; + public render() { + const navigation = _.map(this.props.topLevelMenu, (menuItems: string[], sectionName: string) => { + const finalSectionName = sectionName.replace(/-/g, ' '); + if (this.props.shouldDisplaySectionHeaders) { + const id = utils.getIdFromName(sectionName); + return ( +
+
+ {finalSectionName.toUpperCase()} +
+ {this._renderMenuItems(menuItems)} +
+ ); + } else { + return
{this._renderMenuItems(menuItems)}
; + } + }); + const maxWidthWithScrollbar = 307; + return ( +
+ {this.props.sidebarHeader} + {!_.isUndefined(this.props.versions) && + !_.isUndefined(this.props.selectedVersion) && + !_.isUndefined(this.props.onVersionSelected) && ( +
+ +
+ )} +
{navigation}
+
+ ); + } + private _renderMenuItems(menuItemNames: string[]): React.ReactNode[] { + const menuItemStyles = this.props.shouldDisplaySectionHeaders + ? styles.menuItemWithHeaders + : styles.menuItemWithoutHeaders; + const menuItemInnerDivStyles = this.props.shouldDisplaySectionHeaders ? styles.menuItemInnerDivWithHeaders : {}; + const menuItems = _.map(menuItemNames, menuItemName => { + const id = utils.getIdFromName(menuItemName); + return ( +
+ + + {menuItemName} + + + {this._renderMenuItemSubsections(menuItemName)} +
+ ); + }); + return menuItems; + } + private _renderMenuItemSubsections(menuItemName: string): React.ReactNode { + if (_.isUndefined(this.props.menuSubsectionsBySection[menuItemName])) { + return null; + } + return this._renderMenuSubsectionsBySection(menuItemName, this.props.menuSubsectionsBySection[menuItemName]); + } + private _renderMenuSubsectionsBySection(menuItemName: string, entityNames: string[]): React.ReactNode { + return ( +
    + {_.map(entityNames, entityName => { + const name = `${menuItemName}-${entityName}`; + const id = utils.getIdFromName(name); + return ( +
  • + + + {entityName} + + +
  • + ); + })} +
+ ); + } + private _onMenuItemClick(name: string): void { + const id = utils.getIdFromName(name); + utils.setUrlHash(id); + if (!_.isUndefined(this.props.onMenuItemClick)) { + this.props.onMenuItemClick(); + } + } +} diff --git a/packages/react-shared/src/components/section_header.tsx b/packages/react-shared/src/components/section_header.tsx new file mode 100644 index 000000000..ee34a6c09 --- /dev/null +++ b/packages/react-shared/src/components/section_header.tsx @@ -0,0 +1,73 @@ +import * as React from 'react'; +import { Element as ScrollElement } from 'react-scroll'; + +import { HeaderSizes } from '../types'; +import { colors } from '../utils/colors'; +import { utils } from '../utils/utils'; + +import { AnchorTitle } from './anchor_title'; + +export interface SectionHeaderProps { + sectionName: string; + headerSize?: HeaderSizes; +} + +interface DefaultSectionHeaderProps { + headerSize: HeaderSizes; +} + +type PropsWithDefaults = SectionHeaderProps & DefaultSectionHeaderProps; + +export interface SectionHeaderState { + shouldShowAnchor: boolean; +} + +export class SectionHeader extends React.Component { + public static defaultProps: Partial = { + headerSize: HeaderSizes.H2, + }; + constructor(props: SectionHeaderProps) { + super(props); + this.state = { + shouldShowAnchor: false, + }; + } + public render() { + const { sectionName, headerSize } = this.props as PropsWithDefaults; + + const finalSectionName = this.props.sectionName.replace(/-/g, ' '); + const id = utils.getIdFromName(finalSectionName); + return ( +
+ + + {finalSectionName} + + } + id={id} + shouldShowAnchor={this.state.shouldShowAnchor} + /> + +
+ ); + } + private _setAnchorVisibility(shouldShowAnchor: boolean) { + this.setState({ + shouldShowAnchor, + }); + } +} diff --git a/packages/react-shared/src/components/version_drop_down.tsx b/packages/react-shared/src/components/version_drop_down.tsx new file mode 100644 index 000000000..d9e49b205 --- /dev/null +++ b/packages/react-shared/src/components/version_drop_down.tsx @@ -0,0 +1,39 @@ +import * as _ from 'lodash'; +import DropDownMenu from 'material-ui/DropDownMenu'; +import MenuItem from 'material-ui/MenuItem'; +import * as React from 'react'; + +import { utils } from '../utils/utils'; + +export interface VersionDropDownProps { + selectedVersion: string; + versions: string[]; + onVersionSelected: (semver: string) => void; +} + +export interface VersionDropDownState {} + +export class VersionDropDown extends React.Component { + public render() { + return ( +
+ + {this._renderDropDownItems()} + +
+ ); + } + private _renderDropDownItems() { + const items = _.map(this.props.versions, version => { + return ; + }); + return items; + } + private _updateSelectedVersion(e: any, index: number, semver: string) { + this.props.onVersionSelected(semver); + } +} -- cgit v1.2.3