feat: highlighted sidebar as you scroll on doc reference pages
This commit is contained in:
@@ -20,6 +20,7 @@ export interface MarkdownSectionProps {
|
||||
headerSize?: HeaderSizes;
|
||||
githubLink?: string;
|
||||
shouldReformatTitle?: boolean;
|
||||
alternativeSectionTitle?: string;
|
||||
}
|
||||
|
||||
interface DefaultMarkdownSectionProps {
|
||||
@@ -47,22 +48,25 @@ export class MarkdownSection extends React.Component<MarkdownSectionProps, Markd
|
||||
const { sectionName, markdownContent, headerSize, githubLink } = this.props as PropsWithDefaults;
|
||||
|
||||
const id = utils.getIdFromName(sectionName);
|
||||
const finalSectionName = this.props.shouldReformatTitle
|
||||
const formattedSectionName = this.props.shouldReformatTitle
|
||||
? utils.convertCamelCaseToSpaces(sectionName)
|
||||
: sectionName;
|
||||
const title = !_.isUndefined(this.props.alternativeSectionTitle)
|
||||
? this.props.alternativeSectionTitle
|
||||
: formattedSectionName;
|
||||
return (
|
||||
<div
|
||||
className="md-px1 sm-px2 overflow-hidden"
|
||||
onMouseOver={this._setAnchorVisibility.bind(this, true)}
|
||||
onMouseOut={this._setAnchorVisibility.bind(this, false)}
|
||||
>
|
||||
<ScrollElement name={id}>
|
||||
<Container className="clearfix" marginBottom="20px">
|
||||
<ScrollElement name={id} style={{ paddingBottom: 20 }}>
|
||||
<Container className="clearfix" paddingTop="30px" paddingBottom="20px">
|
||||
<div className="col lg-col-8 md-col-8 sm-col-12">
|
||||
<span style={{ color: colors.grey700 }}>
|
||||
<AnchorTitle
|
||||
headerSize={headerSize}
|
||||
title={_.capitalize(finalSectionName)}
|
||||
title={_.capitalize(title)}
|
||||
id={id}
|
||||
shouldShowAnchor={this.state.shouldShowAnchor}
|
||||
/>
|
||||
@@ -87,13 +91,7 @@ export class MarkdownSection extends React.Component<MarkdownSectionProps, Markd
|
||||
paragraph: MarkdownParagraphBlock,
|
||||
}}
|
||||
/>
|
||||
<Container
|
||||
width={'100%'}
|
||||
height={'1px'}
|
||||
backgroundColor={colors.grey300}
|
||||
marginTop={'32px'}
|
||||
marginBottom={'32px'}
|
||||
/>
|
||||
<Container width={'100%'} height={'1px'} backgroundColor={colors.grey300} marginTop={'32px'} />
|
||||
</ScrollElement>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -14,9 +14,12 @@ export interface NestedSidebarMenuProps {
|
||||
sidebarHeader?: React.ReactNode;
|
||||
shouldDisplaySectionHeaders?: boolean;
|
||||
shouldReformatMenuItemNames?: boolean;
|
||||
parentName?: string;
|
||||
}
|
||||
|
||||
export interface NestedSidebarMenuState {}
|
||||
export interface NestedSidebarMenuState {
|
||||
scrolledToId: string;
|
||||
}
|
||||
|
||||
const styles: Styles = {
|
||||
menuItemWithHeaders: {
|
||||
@@ -40,7 +43,28 @@ export class NestedSidebarMenu extends React.Component<NestedSidebarMenuProps, N
|
||||
public static defaultProps: Partial<NestedSidebarMenuProps> = {
|
||||
shouldDisplaySectionHeaders: true,
|
||||
shouldReformatMenuItemNames: true,
|
||||
parentName: 'default',
|
||||
};
|
||||
private _urlIntervalCheckId: number;
|
||||
constructor(props: NestedSidebarMenuProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
scrolledToId: '',
|
||||
};
|
||||
}
|
||||
public componentDidMount(): void {
|
||||
this._urlIntervalCheckId = window.setInterval(() => {
|
||||
const scrollId = location.hash.slice(1);
|
||||
if (scrollId !== this.state.scrolledToId) {
|
||||
this.setState({
|
||||
scrolledToId: scrollId,
|
||||
});
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
public componentWillUnmount(): void {
|
||||
window.clearInterval(this._urlIntervalCheckId);
|
||||
}
|
||||
public render(): React.ReactNode {
|
||||
const navigation = _.map(this.props.sectionNameToLinks, (links: ALink[], sectionName: string) => {
|
||||
const finalSectionName = utils.convertCamelCaseToSpaces(sectionName);
|
||||
@@ -48,7 +72,7 @@ export class NestedSidebarMenu extends React.Component<NestedSidebarMenuProps, N
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
return (
|
||||
<div key={`section-${sectionName}`} className="py1" style={{ color: colors.greyTheme }}>
|
||||
<div style={{ fontSize: 14, letterSpacing: 0.5 }} className="py1">
|
||||
<div style={{ fontSize: 14, letterSpacing: 0.5 }} className="py1 pl1">
|
||||
{finalSectionName.toUpperCase()}
|
||||
</div>
|
||||
{this._renderMenuItems(links)}
|
||||
@@ -61,23 +85,37 @@ export class NestedSidebarMenu extends React.Component<NestedSidebarMenuProps, N
|
||||
return (
|
||||
<div>
|
||||
{this.props.sidebarHeader}
|
||||
<div className="pl1">{navigation}</div>
|
||||
<div>{navigation}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
private _renderMenuItems(links: ALink[]): React.ReactNode[] {
|
||||
const scrolledToId = this.state.scrolledToId;
|
||||
const menuItemStyles = this.props.shouldDisplaySectionHeaders
|
||||
? styles.menuItemWithHeaders
|
||||
: styles.menuItemWithoutHeaders;
|
||||
const menuItemInnerDivStyles = this.props.shouldDisplaySectionHeaders ? styles.menuItemInnerDivWithHeaders : {};
|
||||
const menuItems = _.map(links, link => {
|
||||
const isScrolledTo = link.to === scrolledToId;
|
||||
const finalMenuItemName = this.props.shouldReformatMenuItemNames
|
||||
? utils.convertDashesToSpaces(link.title)
|
||||
: link.title;
|
||||
return (
|
||||
<div key={`menuItem-${finalMenuItemName}`}>
|
||||
<Link to={link.to} shouldOpenInNewTab={link.shouldOpenInNewTab}>
|
||||
<MenuItem style={menuItemStyles} innerDivStyle={menuItemInnerDivStyles}>
|
||||
<MenuItem
|
||||
style={{
|
||||
...menuItemStyles,
|
||||
paddingLeft: 8,
|
||||
borderRadius: 6,
|
||||
backgroundColor: isScrolledTo ? colors.lightLinkBlue : 'inherit',
|
||||
}}
|
||||
innerDivStyle={{
|
||||
...menuItemInnerDivStyles,
|
||||
color: isScrolledTo ? colors.white : 'inherit',
|
||||
fontWeight: isScrolledTo ? 'bold' : 'inherit',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
textTransform: this.props.shouldReformatMenuItemNames ? 'capitalize' : 'none',
|
||||
|
||||
Reference in New Issue
Block a user