diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index 553c1635242..0bf81792330 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -35,6 +35,7 @@ export default class ScrollableList extends PureComponent {
alwaysPrepend: PropTypes.bool,
emptyMessage: PropTypes.node,
children: PropTypes.node,
+ bindToDocument: PropTypes.bool,
};
static defaultProps = {
@@ -50,7 +51,9 @@ export default class ScrollableList extends PureComponent {
handleScroll = throttle(() => {
if (this.node) {
- const { scrollTop, scrollHeight, clientHeight } = this.node;
+ const scrollTop = this.getScrollTop();
+ const scrollHeight = this.getScrollHeight();
+ const clientHeight = this.getClientHeight();
const offset = scrollHeight - scrollTop - clientHeight;
if (400 > offset && this.props.onLoadMore && this.props.hasMore && !this.props.isLoading) {
@@ -80,9 +83,14 @@ export default class ScrollableList extends PureComponent {
scrollToTopOnMouseIdle = false;
setScrollTop = newScrollTop => {
- if (this.node.scrollTop !== newScrollTop) {
+ if (this.getScrollTop() !== newScrollTop) {
this.lastScrollWasSynthetic = true;
- this.node.scrollTop = newScrollTop;
+
+ if (this.props.bindToDocument) {
+ document.scrollingElement.scrollTop = newScrollTop;
+ } else {
+ this.node.scrollTop = newScrollTop;
+ }
}
};
@@ -100,7 +108,7 @@ export default class ScrollableList extends PureComponent {
this.clearMouseIdleTimer();
this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY);
- if (!this.mouseMovedRecently && this.node.scrollTop === 0) {
+ if (!this.mouseMovedRecently && this.getScrollTop() === 0) {
// Only set if we just started moving and are scrolled to the top.
this.scrollToTopOnMouseIdle = true;
}
@@ -135,15 +143,27 @@ export default class ScrollableList extends PureComponent {
}
getScrollPosition = () => {
- if (this.node && (this.node.scrollTop > 0 || this.mouseMovedRecently)) {
- return { height: this.node.scrollHeight, top: this.node.scrollTop };
+ if (this.node && (this.getScrollTop() > 0 || this.mouseMovedRecently)) {
+ return { height: this.getScrollHeight(), top: this.getScrollTop() };
} else {
return null;
}
}
+ getScrollTop = () => {
+ return this.props.bindToDocument ? document.scrollingElement.scrollTop : this.node.scrollTop;
+ }
+
+ getScrollHeight = () => {
+ return this.props.bindToDocument ? document.scrollingElement.scrollHeight : this.node.scrollHeight;
+ }
+
+ getClientHeight = () => {
+ return this.props.bindToDocument ? document.scrollingElement.clientHeight : this.node.clientHeight;
+ }
+
updateScrollBottom = (snapshot) => {
- const newScrollTop = this.node.scrollHeight - snapshot;
+ const newScrollTop = this.getScrollHeight() - snapshot;
this.setScrollTop(newScrollTop);
}
@@ -153,8 +173,8 @@ export default class ScrollableList extends PureComponent {
React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
- if (someItemInserted && (this.node.scrollTop > 0 || this.mouseMovedRecently)) {
- return this.node.scrollHeight - this.node.scrollTop;
+ if (someItemInserted && (this.getScrollTop() > 0 || this.mouseMovedRecently)) {
+ return this.getScrollHeight() - this.getScrollTop();
} else {
return null;
}
@@ -164,7 +184,7 @@ export default class ScrollableList extends PureComponent {
// Reset the scroll position when a new child comes in in order not to
// jerk the scrollbar around if you're already scrolled down the page.
if (snapshot !== null) {
- this.setScrollTop(this.node.scrollHeight - snapshot);
+ this.setScrollTop(this.getScrollHeight() - snapshot);
}
}
@@ -197,13 +217,23 @@ export default class ScrollableList extends PureComponent {
}
attachScrollListener () {
- this.node.addEventListener('scroll', this.handleScroll);
- this.node.addEventListener('wheel', this.handleWheel);
+ if (this.props.bindToDocument) {
+ document.addEventListener('scroll', this.handleScroll);
+ document.addEventListener('wheel', this.handleWheel);
+ } else {
+ this.node.addEventListener('scroll', this.handleScroll);
+ this.node.addEventListener('wheel', this.handleWheel);
+ }
}
detachScrollListener () {
- this.node.removeEventListener('scroll', this.handleScroll);
- this.node.removeEventListener('wheel', this.handleWheel);
+ if (this.props.bindToDocument) {
+ document.removeEventListener('scroll', this.handleScroll);
+ document.removeEventListener('wheel', this.handleWheel);
+ } else {
+ this.node.removeEventListener('scroll', this.handleScroll);
+ this.node.removeEventListener('wheel', this.handleWheel);
+ }
}
getFirstChildKey (props) {
diff --git a/app/javascript/mastodon/containers/media_container.js b/app/javascript/mastodon/containers/media_container.js
index 51d4f0fed7a..48492f43d01 100644
--- a/app/javascript/mastodon/containers/media_container.js
+++ b/app/javascript/mastodon/containers/media_container.js
@@ -8,6 +8,7 @@ import Video from '../features/video';
import Card from '../features/status/components/card';
import Poll from 'mastodon/components/poll';
import ModalRoot from '../components/modal_root';
+import { getScrollbarWidth } from '../features/ui/components/modal_root';
import MediaModal from '../features/ui/components/media_modal';
import { List as ImmutableList, fromJS } from 'immutable';
@@ -31,6 +32,8 @@ export default class MediaContainer extends PureComponent {
handleOpenMedia = (media, index) => {
document.body.classList.add('with-modals--active');
+ document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
+
this.setState({ media, index });
}
@@ -38,11 +41,15 @@ export default class MediaContainer extends PureComponent {
const media = ImmutableList([video]);
document.body.classList.add('with-modals--active');
+ document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
+
this.setState({ media, time });
}
handleCloseMedia = () => {
document.body.classList.remove('with-modals--active');
+ document.documentElement.style.marginRight = 0;
+
this.setState({ media: null, index: null, time: null });
}
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js
index 27581bfdc88..9914b7e654b 100644
--- a/app/javascript/mastodon/features/account_timeline/index.js
+++ b/app/javascript/mastodon/features/account_timeline/index.js
@@ -44,6 +44,7 @@ class AccountTimeline extends ImmutablePureComponent {
withReplies: PropTypes.bool,
blockedBy: PropTypes.bool,
isAccount: PropTypes.bool,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -77,7 +78,7 @@ class AccountTimeline extends ImmutablePureComponent {
}
render () {
- const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, isAccount } = this.props;
+ const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, isAccount, multiColumn } = this.props;
if (!isAccount) {
return (
@@ -112,6 +113,7 @@ class AccountTimeline extends ImmutablePureComponent {
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
/>
);
diff --git a/app/javascript/mastodon/features/blocks/index.js b/app/javascript/mastodon/features/blocks/index.js
index 96a219c9472..8fb0f051b35 100644
--- a/app/javascript/mastodon/features/blocks/index.js
+++ b/app/javascript/mastodon/features/blocks/index.js
@@ -32,6 +32,7 @@ class Blocks extends ImmutablePureComponent {
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
intl: PropTypes.object.isRequired,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -43,7 +44,7 @@ class Blocks extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
- const { intl, accountIds, shouldUpdateScroll, hasMore } = this.props;
+ const { intl, accountIds, shouldUpdateScroll, hasMore, multiColumn } = this.props;
if (!accountIds) {
return (
@@ -64,6 +65,7 @@ class Blocks extends ImmutablePureComponent {
hasMore={hasMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
>
{accountIds.map(id =>
diff --git a/app/javascript/mastodon/features/community_timeline/index.js b/app/javascript/mastodon/features/community_timeline/index.js
index 7d26c98b0d3..2f6999f6142 100644
--- a/app/javascript/mastodon/features/community_timeline/index.js
+++ b/app/javascript/mastodon/features/community_timeline/index.js
@@ -126,6 +126,7 @@ class CommunityTimeline extends React.PureComponent {
onLoadMore={this.handleLoadMore}
emptyMessage={}
shouldUpdateScroll={shouldUpdateScroll}
+ bindToDocument={!multiColumn}
/>
);
diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.js
index 7c075f5a5c5..16e200b31e4 100644
--- a/app/javascript/mastodon/features/domain_blocks/index.js
+++ b/app/javascript/mastodon/features/domain_blocks/index.js
@@ -33,6 +33,7 @@ class Blocks extends ImmutablePureComponent {
hasMore: PropTypes.bool,
domains: ImmutablePropTypes.orderedSet,
intl: PropTypes.object.isRequired,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -44,7 +45,7 @@ class Blocks extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
- const { intl, domains, shouldUpdateScroll, hasMore } = this.props;
+ const { intl, domains, shouldUpdateScroll, hasMore, multiColumn } = this.props;
if (!domains) {
return (
@@ -65,6 +66,7 @@ class Blocks extends ImmutablePureComponent {
hasMore={hasMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
>
{domains.map(domain =>
diff --git a/app/javascript/mastodon/features/favourited_statuses/index.js b/app/javascript/mastodon/features/favourited_statuses/index.js
index fa9401b90ea..8c7b2386965 100644
--- a/app/javascript/mastodon/features/favourited_statuses/index.js
+++ b/app/javascript/mastodon/features/favourited_statuses/index.js
@@ -95,6 +95,7 @@ class Favourites extends ImmutablePureComponent {
onLoadMore={this.handleLoadMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
/>
);
diff --git a/app/javascript/mastodon/features/favourites/index.js b/app/javascript/mastodon/features/favourites/index.js
index d1ac229a279..464f7aeb0a7 100644
--- a/app/javascript/mastodon/features/favourites/index.js
+++ b/app/javascript/mastodon/features/favourites/index.js
@@ -23,6 +23,7 @@ class Favourites extends ImmutablePureComponent {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -36,7 +37,7 @@ class Favourites extends ImmutablePureComponent {
}
render () {
- const { shouldUpdateScroll, accountIds } = this.props;
+ const { shouldUpdateScroll, accountIds, multiColumn } = this.props;
if (!accountIds) {
return (
@@ -56,6 +57,7 @@ class Favourites extends ImmutablePureComponent {
scrollKey='favourites'
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
>
{accountIds.map(id =>
diff --git a/app/javascript/mastodon/features/follow_requests/index.js b/app/javascript/mastodon/features/follow_requests/index.js
index 44624cb4066..570cf57c8a6 100644
--- a/app/javascript/mastodon/features/follow_requests/index.js
+++ b/app/javascript/mastodon/features/follow_requests/index.js
@@ -32,6 +32,7 @@ class FollowRequests extends ImmutablePureComponent {
hasMore: PropTypes.bool,
accountIds: ImmutablePropTypes.list,
intl: PropTypes.object.isRequired,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -43,7 +44,7 @@ class FollowRequests extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
- const { intl, shouldUpdateScroll, accountIds, hasMore } = this.props;
+ const { intl, shouldUpdateScroll, accountIds, hasMore, multiColumn } = this.props;
if (!accountIds) {
return (
@@ -64,6 +65,7 @@ class FollowRequests extends ImmutablePureComponent {
hasMore={hasMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
>
{accountIds.map(id =>
diff --git a/app/javascript/mastodon/features/followers/index.js b/app/javascript/mastodon/features/followers/index.js
index e3387e1be83..dce05bdc629 100644
--- a/app/javascript/mastodon/features/followers/index.js
+++ b/app/javascript/mastodon/features/followers/index.js
@@ -36,6 +36,7 @@ class Followers extends ImmutablePureComponent {
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
isAccount: PropTypes.bool,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -55,7 +56,7 @@ class Followers extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
- const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount } = this.props;
+ const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn } = this.props;
if (!isAccount) {
return (
@@ -87,6 +88,7 @@ class Followers extends ImmutablePureComponent {
prepend={}
alwaysPrepend
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
>
{blockedBy ? [] : accountIds.map(id =>
diff --git a/app/javascript/mastodon/features/following/index.js b/app/javascript/mastodon/features/following/index.js
index 3bf89fb2bab..d9f2ef0790d 100644
--- a/app/javascript/mastodon/features/following/index.js
+++ b/app/javascript/mastodon/features/following/index.js
@@ -36,6 +36,7 @@ class Following extends ImmutablePureComponent {
hasMore: PropTypes.bool,
blockedBy: PropTypes.bool,
isAccount: PropTypes.bool,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -55,7 +56,7 @@ class Following extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
- const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount } = this.props;
+ const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn } = this.props;
if (!isAccount) {
return (
@@ -87,6 +88,7 @@ class Following extends ImmutablePureComponent {
prepend={}
alwaysPrepend
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
>
{blockedBy ? [] : accountIds.map(id =>
diff --git a/app/javascript/mastodon/features/hashtag_timeline/index.js b/app/javascript/mastodon/features/hashtag_timeline/index.js
index 0d3c97a6489..c50f6a79ae8 100644
--- a/app/javascript/mastodon/features/hashtag_timeline/index.js
+++ b/app/javascript/mastodon/features/hashtag_timeline/index.js
@@ -157,6 +157,7 @@ class HashtagTimeline extends React.PureComponent {
onLoadMore={this.handleLoadMore}
emptyMessage={}
shouldUpdateScroll={shouldUpdateScroll}
+ bindToDocument={!multiColumn}
/>
);
diff --git a/app/javascript/mastodon/features/home_timeline/index.js b/app/javascript/mastodon/features/home_timeline/index.js
index 097f91c16ca..bf8ff117bb7 100644
--- a/app/javascript/mastodon/features/home_timeline/index.js
+++ b/app/javascript/mastodon/features/home_timeline/index.js
@@ -119,6 +119,7 @@ class HomeTimeline extends React.PureComponent {
timelineId='home'
emptyMessage={ }} />}
shouldUpdateScroll={shouldUpdateScroll}
+ bindToDocument={!multiColumn}
/>
);
diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js
index 0db6d222829..844c93db1e5 100644
--- a/app/javascript/mastodon/features/list_timeline/index.js
+++ b/app/javascript/mastodon/features/list_timeline/index.js
@@ -184,6 +184,7 @@ class ListTimeline extends React.PureComponent {
onLoadMore={this.handleLoadMore}
emptyMessage={}
shouldUpdateScroll={shouldUpdateScroll}
+ bindToDocument={!multiColumn}
/>
);
diff --git a/app/javascript/mastodon/features/lists/index.js b/app/javascript/mastodon/features/lists/index.js
index 015e21b6876..a06e0b934f7 100644
--- a/app/javascript/mastodon/features/lists/index.js
+++ b/app/javascript/mastodon/features/lists/index.js
@@ -40,6 +40,7 @@ class Lists extends ImmutablePureComponent {
dispatch: PropTypes.func.isRequired,
lists: ImmutablePropTypes.list,
intl: PropTypes.object.isRequired,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -47,7 +48,7 @@ class Lists extends ImmutablePureComponent {
}
render () {
- const { intl, shouldUpdateScroll, lists } = this.props;
+ const { intl, shouldUpdateScroll, lists, multiColumn } = this.props;
if (!lists) {
return (
@@ -70,6 +71,7 @@ class Lists extends ImmutablePureComponent {
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
prepend={}
+ bindToDocument={!multiColumn}
>
{lists.map(list =>
diff --git a/app/javascript/mastodon/features/mutes/index.js b/app/javascript/mastodon/features/mutes/index.js
index 4ed29a1ce67..57d8b9915f4 100644
--- a/app/javascript/mastodon/features/mutes/index.js
+++ b/app/javascript/mastodon/features/mutes/index.js
@@ -32,6 +32,7 @@ class Mutes extends ImmutablePureComponent {
hasMore: PropTypes.bool,
accountIds: ImmutablePropTypes.list,
intl: PropTypes.object.isRequired,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -43,7 +44,7 @@ class Mutes extends ImmutablePureComponent {
}, 300, { leading: true });
render () {
- const { intl, shouldUpdateScroll, hasMore, accountIds } = this.props;
+ const { intl, shouldUpdateScroll, hasMore, accountIds, multiColumn } = this.props;
if (!accountIds) {
return (
@@ -64,6 +65,7 @@ class Mutes extends ImmutablePureComponent {
hasMore={hasMore}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
>
{accountIds.map(id =>
diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js
index df4ad6f2aab..e708c4fcf15 100644
--- a/app/javascript/mastodon/features/notifications/index.js
+++ b/app/javascript/mastodon/features/notifications/index.js
@@ -191,6 +191,7 @@ class Notifications extends React.PureComponent {
onScrollToTop={this.handleScrollToTop}
onScroll={this.handleScroll}
shouldUpdateScroll={shouldUpdateScroll}
+ bindToDocument={!multiColumn}
>
{scrollableContent}
diff --git a/app/javascript/mastodon/features/pinned_statuses/index.js b/app/javascript/mastodon/features/pinned_statuses/index.js
index 98cdbda3c49..64ebfc7ae56 100644
--- a/app/javascript/mastodon/features/pinned_statuses/index.js
+++ b/app/javascript/mastodon/features/pinned_statuses/index.js
@@ -28,6 +28,7 @@ class PinnedStatuses extends ImmutablePureComponent {
statusIds: ImmutablePropTypes.list.isRequired,
intl: PropTypes.object.isRequired,
hasMore: PropTypes.bool.isRequired,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -43,7 +44,7 @@ class PinnedStatuses extends ImmutablePureComponent {
}
render () {
- const { intl, shouldUpdateScroll, statusIds, hasMore } = this.props;
+ const { intl, shouldUpdateScroll, statusIds, hasMore, multiColumn } = this.props;
return (
@@ -53,6 +54,7 @@ class PinnedStatuses extends ImmutablePureComponent {
scrollKey='pinned_statuses'
hasMore={hasMore}
shouldUpdateScroll={shouldUpdateScroll}
+ bindToDocument={!multiColumn}
/>
);
diff --git a/app/javascript/mastodon/features/public_timeline/index.js b/app/javascript/mastodon/features/public_timeline/index.js
index 2b7d9c56f99..1edb303b84d 100644
--- a/app/javascript/mastodon/features/public_timeline/index.js
+++ b/app/javascript/mastodon/features/public_timeline/index.js
@@ -126,6 +126,7 @@ class PublicTimeline extends React.PureComponent {
scrollKey={`public_timeline-${columnId}`}
emptyMessage={}
shouldUpdateScroll={shouldUpdateScroll}
+ bindToDocument={!multiColumn}
/>
);
diff --git a/app/javascript/mastodon/features/reblogs/index.js b/app/javascript/mastodon/features/reblogs/index.js
index c05d21c7404..26f93ad1b21 100644
--- a/app/javascript/mastodon/features/reblogs/index.js
+++ b/app/javascript/mastodon/features/reblogs/index.js
@@ -23,6 +23,7 @@ class Reblogs extends ImmutablePureComponent {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
accountIds: ImmutablePropTypes.list,
+ multiColumn: PropTypes.bool,
};
componentWillMount () {
@@ -36,7 +37,7 @@ class Reblogs extends ImmutablePureComponent {
}
render () {
- const { shouldUpdateScroll, accountIds } = this.props;
+ const { shouldUpdateScroll, accountIds, multiColumn } = this.props;
if (!accountIds) {
return (
@@ -56,6 +57,7 @@ class Reblogs extends ImmutablePureComponent {
scrollKey='reblogs'
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
+ bindToDocument={!multiColumn}
>
{accountIds.map(id =>
diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js
index cc2ab6c8ce9..06f9e1bc4d4 100644
--- a/app/javascript/mastodon/features/ui/components/modal_root.js
+++ b/app/javascript/mastodon/features/ui/components/modal_root.js
@@ -32,6 +32,28 @@ const MODAL_COMPONENTS = {
'LIST_ADDER':ListAdder,
};
+let cachedScrollbarWidth = null;
+
+export const getScrollbarWidth = () => {
+ if (cachedScrollbarWidth !== null) {
+ return cachedScrollbarWidth;
+ }
+
+ const outer = document.createElement('div');
+ outer.style.visibility = 'hidden';
+ outer.style.overflow = 'scroll';
+ document.body.appendChild(outer);
+
+ const inner = document.createElement('div');
+ outer.appendChild(inner);
+
+ const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
+ cachedScrollbarWidth = scrollbarWidth;
+ outer.parentNode.removeChild(outer);
+
+ return scrollbarWidth;
+};
+
export default class ModalRoot extends React.PureComponent {
static propTypes = {
@@ -47,8 +69,10 @@ export default class ModalRoot extends React.PureComponent {
componentDidUpdate (prevProps, prevState, { visible }) {
if (visible) {
document.body.classList.add('with-modals--active');
+ document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
} else {
document.body.classList.remove('with-modals--active');
+ document.documentElement.style.marginRight = 0;
}
}
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index 791133afd32..d1a3dc9495f 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -110,12 +110,25 @@ class SwitchingColumnsArea extends React.PureComponent {
componentWillMount () {
window.addEventListener('resize', this.handleResize, { passive: true });
+
+ if (this.state.mobile || forceSingleColumn) {
+ document.body.classList.toggle('layout-single-column', true);
+ document.body.classList.toggle('layout-multiple-columns', false);
+ } else {
+ document.body.classList.toggle('layout-single-column', false);
+ document.body.classList.toggle('layout-multiple-columns', true);
+ }
}
- componentDidUpdate (prevProps) {
+ componentDidUpdate (prevProps, prevState) {
if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
this.node.handleChildrenContentChange();
}
+
+ if (prevState.mobile !== this.state.mobile && !forceSingleColumn) {
+ document.body.classList.toggle('layout-single-column', this.state.mobile);
+ document.body.classList.toggle('layout-multiple-columns', !this.state.mobile);
+ }
}
componentWillUnmount () {
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index 2924413855b..0c60d828e9a 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -108,14 +108,6 @@ function main() {
if (parallaxComponents.length > 0 ) {
new Rellax('.parallax', { speed: -1 });
}
-
- if (document.body.classList.contains('with-modals')) {
- const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
- const scrollbarWidthStyle = document.createElement('style');
- scrollbarWidthStyle.id = 'scrollbar-width';
- document.head.appendChild(scrollbarWidthStyle);
- scrollbarWidthStyle.sheet.insertRule(`body.with-modals--active { margin-right: ${scrollbarWidth}px; }`, 0);
- }
});
delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index b5a77ce94fe..7df76bdff6e 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -8,7 +8,7 @@
body {
font-family: $font-sans-serif, sans-serif;
- background: darken($ui-base-color, 8%);
+ background: darken($ui-base-color, 7%);
font-size: 13px;
line-height: 18px;
font-weight: 400;
@@ -35,11 +35,19 @@ body {
}
&.app-body {
- position: absolute;
- width: 100%;
- height: 100%;
padding: 0;
- background: $ui-base-color;
+
+ &.layout-single-column {
+ height: auto;
+ min-height: 100%;
+ overflow-y: scroll;
+ }
+
+ &.layout-multiple-columns {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ }
&.with-modals--active {
overflow-y: hidden;
@@ -56,7 +64,6 @@ body {
&--active {
overflow-y: hidden;
- margin-right: 13px;
}
}
@@ -134,9 +141,22 @@ button {
& > div {
display: flex;
width: 100%;
- height: 100%;
align-items: center;
justify-content: center;
outline: 0 !important;
}
}
+
+.layout-single-column .app-holder {
+ &,
+ & > div {
+ min-height: 100%;
+ }
+}
+
+.layout-multiple-columns .app-holder {
+ &,
+ & > div {
+ height: 100%;
+ }
+}
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index e413b00131b..4eb4e78d638 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -1804,6 +1804,7 @@ a.account__display-name {
justify-content: center;
width: 100%;
height: 100%;
+ min-height: 100vh;
&__pane {
height: 100%;
@@ -1817,6 +1818,7 @@ a.account__display-name {
}
&__inner {
+ position: fixed;
width: 285px;
pointer-events: auto;
height: 100%;
@@ -1871,7 +1873,6 @@ a.account__display-name {
flex-direction: column;
width: 100%;
height: 100%;
- background: darken($ui-base-color, 7%);
}
.drawer {
@@ -2012,6 +2013,10 @@ a.account__display-name {
top: 15px;
}
+ .scrollable {
+ overflow: visible;
+ }
+
@media screen and (min-width: $no-gap-breakpoint) {
padding: 10px 0;
}