Skip to content

Commit e9d0884

Browse files
committed
PR Changes
1 parent 63280cc commit e9d0884

File tree

7 files changed

+210
-182
lines changed

7 files changed

+210
-182
lines changed

src/components/MarkdownPage/MarkdownPage.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import createOgUrl from 'utils/createOgUrl';
2525
const MarkdownPage = ({
2626
authors,
2727
createLink,
28-
date,
28+
date,
29+
enableScrollSync,
2930
ogDescription,
3031
location,
3132
markdownRemark,
@@ -98,6 +99,7 @@ const MarkdownPage = ({
9899

99100
<div css={sharedStyles.articleLayout.sidebar}>
100101
<StickyResponsiveSidebar
102+
enableScrollSync={enableScrollSync}
101103
createLink={createLink}
102104
defaultActiveSection={findSectionForPath(
103105
location.pathname,
@@ -131,7 +133,8 @@ MarkdownPage.defaultProps = {
131133
MarkdownPage.propTypes = {
132134
authors: PropTypes.array.isRequired,
133135
createLink: PropTypes.func.isRequired,
134-
date: PropTypes.string,
136+
date: PropTypes.string,
137+
enableScrollSync: PropTypes.bool,
135138
location: PropTypes.object.isRequired,
136139
markdownRemark: PropTypes.object.isRequired,
137140
sectionList: PropTypes.array.isRequired,
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* Copyright (c) 2013-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the CC-BY-4.0 license found
5+
* in the LICENSE file in the root directory of this source tree.
6+
*
7+
* @emails react-core
8+
*/
9+
10+
'use strict';
11+
12+
import React, {Component} from 'react';
13+
import Section from './Section';
14+
15+
class ScrollSyncSection extends Component {
16+
constructor(props, context) {
17+
super(props, context);
18+
19+
this.state = {
20+
activeItemId: '',
21+
itemTopOffsets: [],
22+
};
23+
24+
this.calculateItemTopOffsets = this.calculateItemTopOffsets.bind(this);
25+
this.handleResize = this.handleResize.bind(this);
26+
this.handleScroll = this.handleScroll.bind(this);
27+
}
28+
29+
componentDidMount() {
30+
this.calculateItemTopOffsets();
31+
32+
window.addEventListener('resize', this.handleResize);
33+
window.addEventListener('scroll', this.handleScroll);
34+
}
35+
36+
componentWillUnmount() {
37+
window.removeEventListener('resize', this.handleResize);
38+
window.removeEventListener('scroll', this.handleScroll);
39+
}
40+
41+
calculateItemTopOffsets() {
42+
const {section} = this.props;
43+
44+
const itemIds = _getItemIds(section.items);
45+
this.setState({
46+
itemTopOffsets: _getElementTopOffsetsById(itemIds),
47+
});
48+
}
49+
50+
handleResize() {
51+
this.calculateItemTopOffsets();
52+
this.handleScroll();
53+
}
54+
55+
handleScroll() {
56+
const {itemTopOffsets} = this.state;
57+
const item = itemTopOffsets.find((itemTopOffset, i) => {
58+
const nextItemTopOffset = itemTopOffsets[i + 1];
59+
if (nextItemTopOffset) {
60+
return (
61+
window.scrollY >= itemTopOffset.offsetTop &&
62+
window.scrollY < nextItemTopOffset.offsetTop
63+
);
64+
}
65+
return window.scrollY >= itemTopOffset.offsetTop;
66+
});
67+
this.setState({
68+
activeItemId: item ? item.id : '',
69+
});
70+
}
71+
72+
render() {
73+
const {activeItemId} = this.state;
74+
return (
75+
<Section
76+
isScrollSync
77+
activeItemId={activeItemId} {...this.props}
78+
/>
79+
);
80+
}
81+
}
82+
83+
const _getItemIds = items =>
84+
items
85+
.map(item => {
86+
let subItemIds = [];
87+
if (item.subitems) {
88+
subItemIds = item.subitems.map(subitem => subitem.id);
89+
}
90+
return [item.id, ...subItemIds];
91+
})
92+
.reduce((prev, current) => prev.concat(current));
93+
94+
const _getElementTopOffsetsById = ids =>
95+
ids
96+
.map(id => {
97+
const element = document.getElementById(id);
98+
if (!element) {
99+
return null;
100+
}
101+
return {
102+
id,
103+
offsetTop: element.offsetTop,
104+
};
105+
})
106+
.filter(item => item);
107+
108+
export default ScrollSyncSection;

src/templates/components/Sidebar/Section.js

Lines changed: 78 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -9,161 +9,89 @@
99

1010
'use strict';
1111

12-
import React, {Component} from 'react';
1312
import {colors, media} from 'theme';
13+
import isItemActive from 'utils/isItemActive';
1414
import MetaTitle from '../MetaTitle';
1515
import ChevronSvg from '../ChevronSvg';
1616

17-
class Section extends Component {
18-
constructor(props, context) {
19-
super(props, context);
20-
21-
this.state = {
22-
activeItemId: null,
23-
itemTopOffsets: [],
24-
};
25-
26-
this.handleScroll = this.handleScroll.bind(this);
27-
}
28-
29-
componentDidMount() {
30-
const {section} = this.props;
31-
32-
const itemIds = _getItemIds(section.items);
33-
this.setState({
34-
itemTopOffsets: _getElementTopOffsetsById(itemIds),
35-
});
36-
37-
window.addEventListener('scroll', this.handleScroll);
38-
}
39-
40-
componentWillUnmount() {
41-
window.removeEventListener('scroll', this.handleScroll);
42-
}
43-
44-
handleScroll() {
45-
const {itemTopOffsets} = this.state;
46-
const item = itemTopOffsets.find((itemTopOffset, i) => {
47-
const nextItemTopOffset = itemTopOffsets[i + 1];
48-
if (nextItemTopOffset) {
49-
return (
50-
window.scrollY >= itemTopOffset.offsetTop &&
51-
window.scrollY < nextItemTopOffset.offsetTop
52-
);
53-
}
54-
return window.scrollY >= itemTopOffset.offsetTop;
55-
});
56-
this.setState({
57-
activeItemId: item ? item.id : null,
58-
});
59-
}
60-
61-
render() {
62-
const {
63-
createLink,
64-
isActive,
65-
location,
66-
onLinkClick,
67-
onSectionTitleClick,
68-
section,
69-
} = this.props;
70-
const {activeItemId} = this.state;
71-
return (
72-
<div>
73-
<MetaTitle
74-
onClick={onSectionTitleClick}
75-
cssProps={{
76-
marginTop: 10,
77-
78-
[media.greaterThan('small')]: {
79-
color: isActive ? colors.text : colors.subtle,
80-
81-
':hover': {
82-
color: colors.text,
83-
},
84-
},
85-
}}>
86-
{section.title}
87-
<ChevronSvg
88-
cssProps={{
89-
marginLeft: 7,
90-
transform: isActive ? 'rotateX(180deg)' : 'rotateX(0deg)',
91-
transition: 'transform 0.2s ease',
92-
93-
[media.lessThan('small')]: {
94-
display: 'none',
95-
},
96-
}}
97-
/>
98-
</MetaTitle>
99-
<ul
17+
const Section = ({
18+
activeItemId,
19+
createLink,
20+
isActive,
21+
isScrollSync,
22+
location,
23+
onLinkClick,
24+
onSectionTitleClick,
25+
section,
26+
}) => (
27+
<div>
28+
<MetaTitle
29+
onClick={onSectionTitleClick}
30+
cssProps={{
31+
marginTop: 10,
32+
33+
[media.greaterThan('small')]: {
34+
color: isActive ? colors.text : colors.subtle,
35+
36+
':hover': {
37+
color: colors.text,
38+
},
39+
},
40+
}}>
41+
{section.title}
42+
<ChevronSvg
43+
cssProps={{
44+
marginLeft: 7,
45+
transform: isActive ? 'rotateX(180deg)' : 'rotateX(0deg)',
46+
transition: 'transform 0.2s ease',
47+
48+
[media.lessThan('small')]: {
49+
display: 'none',
50+
},
51+
}}
52+
/>
53+
</MetaTitle>
54+
<ul
55+
css={{
56+
marginBottom: 10,
57+
58+
[media.greaterThan('small')]: {
59+
display: isActive ? 'block' : 'none',
60+
},
61+
}}>
62+
{section.items.map(item => (
63+
<li
64+
key={item.id}
10065
css={{
101-
marginBottom: 10,
102-
103-
[media.greaterThan('small')]: {
104-
display: isActive ? 'block' : 'none',
105-
},
66+
marginTop: 5,
10667
}}>
107-
{section.items.map(item => (
108-
<li
109-
key={item.id}
110-
css={{
111-
marginTop: 5,
112-
}}>
113-
{createLink({
114-
item,
115-
location,
116-
onLinkClick,
117-
section,
118-
isActive: activeItemId === item.id,
119-
})}
120-
121-
{item.subitems && (
122-
<ul css={{marginLeft: 20}}>
123-
{item.subitems.map(subitem => (
124-
<li key={subitem.id}>
125-
{createLink({
126-
item: subitem,
127-
location,
128-
onLinkClick,
129-
section,
130-
isActive: activeItemId === subitem.id,
131-
})}
132-
</li>
133-
))}
134-
</ul>
135-
)}
136-
</li>
137-
))}
138-
</ul>
139-
</div>
140-
);
141-
}
142-
}
143-
144-
const _getItemIds = items =>
145-
items
146-
.map(item => {
147-
let subItemIds = [];
148-
if (item.subitems) {
149-
subItemIds = item.subitems.map(subitem => subitem.id);
150-
}
151-
return [item.id, ...subItemIds];
152-
})
153-
.reduce((prev, current) => prev.concat(current));
154-
155-
const _getElementTopOffsetsById = ids =>
156-
ids
157-
.map(id => {
158-
const element = document.getElementById(id);
159-
if (!element) {
160-
return null;
161-
}
162-
return {
163-
id,
164-
offsetTop: element.offsetTop,
165-
};
166-
})
167-
.filter(item => item);
68+
{createLink({
69+
isActive: isScrollSync ? activeItemId === item.id : isItemActive(location, item),
70+
item,
71+
location,
72+
onLinkClick,
73+
section,
74+
})}
75+
76+
{item.subitems && (
77+
<ul css={{marginLeft: 20}}>
78+
{item.subitems.map(subitem => (
79+
<li key={subitem.id}>
80+
{createLink({
81+
isActive: isScrollSync ? activeItemId === subitem.id : isItemActive(location, subitem),
82+
item: subitem,
83+
location,
84+
onLinkClick,
85+
section,
86+
})}
87+
</li>
88+
))}
89+
</ul>
90+
)}
91+
</li>
92+
))}
93+
</ul>
94+
</div>
95+
);
16896

16997
export default Section;

0 commit comments

Comments
 (0)