forked from cybrespace/mastodon
		
	Fix scroll to top in single column UI (#11463)
This commit is contained in:
		
							parent
							
								
									8b9d0a0533
								
							
						
					
					
						commit
						2dee293c4c
					
				
					 26 changed files with 55 additions and 30 deletions
				
			
		|  | @ -8,10 +8,11 @@ export default class Column extends React.PureComponent { | ||||||
|   static propTypes = { |   static propTypes = { | ||||||
|     children: PropTypes.node, |     children: PropTypes.node, | ||||||
|     label: PropTypes.string, |     label: PropTypes.string, | ||||||
|  |     bindToDocument: PropTypes.bool, | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   scrollTop () { |   scrollTop () { | ||||||
|     const scrollable = this.node.querySelector('.scrollable'); |     const scrollable = this.props.bindToDocument ? document.scrollingElement : this.node.querySelector('.scrollable'); | ||||||
| 
 | 
 | ||||||
|     if (!scrollable) { |     if (!scrollable) { | ||||||
|       return; |       return; | ||||||
|  | @ -33,12 +34,20 @@ export default class Column extends React.PureComponent { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   componentDidMount () { |   componentDidMount () { | ||||||
|  |     if (this.props.bindToDocument) { | ||||||
|  |       document.addEventListener('wheel', this.handleWheel,  detectPassiveEvents.hasSupport ? { passive: true } : false); | ||||||
|  |     } else { | ||||||
|       this.node.addEventListener('wheel', this.handleWheel,  detectPassiveEvents.hasSupport ? { passive: true } : false); |       this.node.addEventListener('wheel', this.handleWheel,  detectPassiveEvents.hasSupport ? { passive: true } : false); | ||||||
|     } |     } | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   componentWillUnmount () { |   componentWillUnmount () { | ||||||
|  |     if (this.props.bindToDocument) { | ||||||
|  |       document.removeEventListener('wheel', this.handleWheel); | ||||||
|  |     } else { | ||||||
|       this.node.removeEventListener('wheel', this.handleWheel); |       this.node.removeEventListener('wheel', this.handleWheel); | ||||||
|     } |     } | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     const { label, children } = this.props; |     const { label, children } = this.props; | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import React from 'react'; | ||||||
| import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import Icon from 'mastodon/components/icon'; | import Icon from 'mastodon/components/icon'; | ||||||
|  | import { createPortal } from 'react-dom'; | ||||||
| 
 | 
 | ||||||
| export default class ColumnBackButton extends React.PureComponent { | export default class ColumnBackButton extends React.PureComponent { | ||||||
| 
 | 
 | ||||||
|  | @ -9,6 +10,10 @@ export default class ColumnBackButton extends React.PureComponent { | ||||||
|     router: PropTypes.object, |     router: PropTypes.object, | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  |   static propTypes = { | ||||||
|  |     multiColumn: PropTypes.bool, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|   handleClick = () => { |   handleClick = () => { | ||||||
|     if (window.history && window.history.length === 1) { |     if (window.history && window.history.length === 1) { | ||||||
|       this.context.router.history.push('/'); |       this.context.router.history.push('/'); | ||||||
|  | @ -18,12 +23,20 @@ export default class ColumnBackButton extends React.PureComponent { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     return ( |     const { multiColumn } = this.props; | ||||||
|  | 
 | ||||||
|  |     const component = ( | ||||||
|       <button onClick={this.handleClick} className='column-back-button'> |       <button onClick={this.handleClick} className='column-back-button'> | ||||||
|         <Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> |         <Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> | ||||||
|         <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> |         <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> | ||||||
|       </button> |       </button> | ||||||
|     ); |     ); | ||||||
|  | 
 | ||||||
|  |     if (multiColumn) { | ||||||
|  |       return component; | ||||||
|  |     } else { | ||||||
|  |       return createPortal(component, document.getElementById('tabs-bar__portal')); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -56,6 +56,7 @@ class AccountGallery extends ImmutablePureComponent { | ||||||
|     isLoading: PropTypes.bool, |     isLoading: PropTypes.bool, | ||||||
|     hasMore: PropTypes.bool, |     hasMore: PropTypes.bool, | ||||||
|     isAccount: PropTypes.bool, |     isAccount: PropTypes.bool, | ||||||
|  |     multiColumn: PropTypes.bool, | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   state = { |   state = { | ||||||
|  | @ -116,7 +117,7 @@ class AccountGallery extends ImmutablePureComponent { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     const { attachments, shouldUpdateScroll, isLoading, hasMore, isAccount } = this.props; |     const { attachments, shouldUpdateScroll, isLoading, hasMore, isAccount, multiColumn } = this.props; | ||||||
|     const { width } = this.state; |     const { width } = this.state; | ||||||
| 
 | 
 | ||||||
|     if (!isAccount) { |     if (!isAccount) { | ||||||
|  | @ -143,7 +144,7 @@ class AccountGallery extends ImmutablePureComponent { | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column> |       <Column> | ||||||
|         <ColumnBackButton /> |         <ColumnBackButton multiColumn={multiColumn} /> | ||||||
| 
 | 
 | ||||||
|         <ScrollContainer scrollKey='account_gallery' shouldUpdateScroll={shouldUpdateScroll}> |         <ScrollContainer scrollKey='account_gallery' shouldUpdateScroll={shouldUpdateScroll}> | ||||||
|           <div className='scrollable scrollable--flex' onScroll={this.handleScroll}> |           <div className='scrollable scrollable--flex' onScroll={this.handleScroll}> | ||||||
|  |  | ||||||
|  | @ -100,7 +100,7 @@ class AccountTimeline extends ImmutablePureComponent { | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column> |       <Column> | ||||||
|         <ColumnBackButton /> |         <ColumnBackButton multiColumn={multiColumn} /> | ||||||
| 
 | 
 | ||||||
|         <StatusList |         <StatusList | ||||||
|           prepend={<HeaderContainer accountId={this.props.params.accountId} />} |           prepend={<HeaderContainer accountId={this.props.params.accountId} />} | ||||||
|  |  | ||||||
|  | @ -57,7 +57,7 @@ class Blocks extends ImmutablePureComponent { | ||||||
|     const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />; |     const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column icon='ban' heading={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} icon='ban' heading={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|           scrollKey='blocks' |           scrollKey='blocks' | ||||||
|  |  | ||||||
|  | @ -105,7 +105,7 @@ class CommunityTimeline extends React.PureComponent { | ||||||
|     const pinned = !!columnId; |     const pinned = !!columnId; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='users' |           icon='users' | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|  |  | ||||||
|  | @ -75,7 +75,7 @@ class DirectTimeline extends React.PureComponent { | ||||||
|     const pinned = !!columnId; |     const pinned = !!columnId; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='envelope' |           icon='envelope' | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ class Blocks extends ImmutablePureComponent { | ||||||
|     const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no hidden domains yet.' />; |     const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no hidden domains yet.' />; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column icon='minus-circle' heading={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} icon='minus-circle' heading={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|           scrollKey='domain_blocks' |           scrollKey='domain_blocks' | ||||||
|  |  | ||||||
|  | @ -74,7 +74,7 @@ class Favourites extends ImmutablePureComponent { | ||||||
|     const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite toots yet. When you favourite one, it will show up here." />; |     const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite toots yet. When you favourite one, it will show up here." />; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column ref={this.setRef} label={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='star' |           icon='star' | ||||||
|           title={intl.formatMessage(messages.heading)} |           title={intl.formatMessage(messages.heading)} | ||||||
|  |  | ||||||
|  | @ -51,7 +51,7 @@ class Favourites extends ImmutablePureComponent { | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column> |       <Column> | ||||||
|         <ColumnBackButton /> |         <ColumnBackButton multiColumn={multiColumn} /> | ||||||
| 
 | 
 | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|           scrollKey='favourites' |           scrollKey='favourites' | ||||||
|  |  | ||||||
|  | @ -57,7 +57,7 @@ class FollowRequests extends ImmutablePureComponent { | ||||||
|     const emptyMessage = <FormattedMessage id='empty_column.follow_requests' defaultMessage="You don't have any follow requests yet. When you receive one, it will show up here." />; |     const emptyMessage = <FormattedMessage id='empty_column.follow_requests' defaultMessage="You don't have any follow requests yet. When you receive one, it will show up here." />; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column icon='user-plus' heading={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} icon='user-plus' heading={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|           scrollKey='follow_requests' |           scrollKey='follow_requests' | ||||||
|  |  | ||||||
|  | @ -78,7 +78,7 @@ class Followers extends ImmutablePureComponent { | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column> |       <Column> | ||||||
|         <ColumnBackButton /> |         <ColumnBackButton multiColumn={multiColumn} /> | ||||||
| 
 | 
 | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|           scrollKey='followers' |           scrollKey='followers' | ||||||
|  |  | ||||||
|  | @ -78,7 +78,7 @@ class Following extends ImmutablePureComponent { | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column> |       <Column> | ||||||
|         <ColumnBackButton /> |         <ColumnBackButton multiColumn={multiColumn} /> | ||||||
| 
 | 
 | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|           scrollKey='following' |           scrollKey='following' | ||||||
|  |  | ||||||
|  | @ -148,7 +148,7 @@ class GettingStarted extends ImmutablePureComponent { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column label={intl.formatMessage(messages.menu)}> |       <Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.menu)}> | ||||||
|         {multiColumn && <div className='column-header__wrapper'> |         {multiColumn && <div className='column-header__wrapper'> | ||||||
|           <h1 className='column-header'> |           <h1 className='column-header'> | ||||||
|             <button> |             <button> | ||||||
|  |  | ||||||
|  | @ -135,7 +135,7 @@ class HashtagTimeline extends React.PureComponent { | ||||||
|     const pinned = !!columnId; |     const pinned = !!columnId; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column ref={this.setRef} label={`#${id}`}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='hashtag' |           icon='hashtag' | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|  |  | ||||||
|  | @ -98,7 +98,7 @@ class HomeTimeline extends React.PureComponent { | ||||||
|     const pinned = !!columnId; |     const pinned = !!columnId; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='home' |           icon='home' | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|  |  | ||||||
|  | @ -18,10 +18,10 @@ class KeyboardShortcuts extends ImmutablePureComponent { | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     const { intl } = this.props; |     const { intl, multiColumn } = this.props; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column icon='question' heading={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} icon='question' heading={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
|         <div className='keyboard-shortcuts scrollable optionally-scrollable'> |         <div className='keyboard-shortcuts scrollable optionally-scrollable'> | ||||||
|           <table> |           <table> | ||||||
|  |  | ||||||
|  | @ -148,14 +148,14 @@ class ListTimeline extends React.PureComponent { | ||||||
|     } else if (list === false) { |     } else if (list === false) { | ||||||
|       return ( |       return ( | ||||||
|         <Column> |         <Column> | ||||||
|           <ColumnBackButton /> |           <ColumnBackButton multiColumn={multiColumn} /> | ||||||
|           <MissingIndicator /> |           <MissingIndicator /> | ||||||
|         </Column> |         </Column> | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column ref={this.setRef} label={title}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={title}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='list-ul' |           icon='list-ul' | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|  |  | ||||||
|  | @ -61,7 +61,7 @@ class Lists extends ImmutablePureComponent { | ||||||
|     const emptyMessage = <FormattedMessage id='empty_column.lists' defaultMessage="You don't have any lists yet. When you create one, it will show up here." />; |     const emptyMessage = <FormattedMessage id='empty_column.lists' defaultMessage="You don't have any lists yet. When you create one, it will show up here." />; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column icon='list-ul' heading={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} icon='list-ul' heading={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
| 
 | 
 | ||||||
|         <NewListForm /> |         <NewListForm /> | ||||||
|  |  | ||||||
|  | @ -57,7 +57,7 @@ class Mutes extends ImmutablePureComponent { | ||||||
|     const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />; |     const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column icon='volume-off' heading={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} icon='volume-off' heading={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|           scrollKey='mutes' |           scrollKey='mutes' | ||||||
|  |  | ||||||
|  | @ -198,7 +198,7 @@ class Notifications extends React.PureComponent { | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column ref={this.setColumnRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setColumnRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='bell' |           icon='bell' | ||||||
|           active={isUnread} |           active={isUnread} | ||||||
|  |  | ||||||
|  | @ -47,7 +47,7 @@ class PinnedStatuses extends ImmutablePureComponent { | ||||||
|     const { intl, shouldUpdateScroll, statusIds, hasMore, multiColumn } = this.props; |     const { intl, shouldUpdateScroll, statusIds, hasMore, multiColumn } = this.props; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}> |       <Column bindToDocument={!multiColumn} icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
|         <StatusList |         <StatusList | ||||||
|           statusIds={statusIds} |           statusIds={statusIds} | ||||||
|  |  | ||||||
|  | @ -105,7 +105,7 @@ class PublicTimeline extends React.PureComponent { | ||||||
|     const pinned = !!columnId; |     const pinned = !!columnId; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='globe' |           icon='globe' | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|  |  | ||||||
|  | @ -51,7 +51,7 @@ class Reblogs extends ImmutablePureComponent { | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column> |       <Column> | ||||||
|         <ColumnBackButton /> |         <ColumnBackButton multiColumn={multiColumn} /> | ||||||
| 
 | 
 | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|           scrollKey='reblogs' |           scrollKey='reblogs' | ||||||
|  |  | ||||||
|  | @ -471,7 +471,7 @@ class Status extends ImmutablePureComponent { | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column label={intl.formatMessage(messages.detailedStatus)}> |       <Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.detailedStatus)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           showBackButton |           showBackButton | ||||||
|           multiColumn={multiColumn} |           multiColumn={multiColumn} | ||||||
|  |  | ||||||
|  | @ -2411,6 +2411,8 @@ a.account__display-name { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .column-back-button { | .column-back-button { | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   width: 100%; | ||||||
|   background: lighten($ui-base-color, 4%); |   background: lighten($ui-base-color, 4%); | ||||||
|   color: $highlight-text-color; |   color: $highlight-text-color; | ||||||
|   cursor: pointer; |   cursor: pointer; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue