TimelineQueueButtonHeader: display only when scrolled down
This commit is contained in:
parent
bba3564ef3
commit
4840a3c751
3 changed files with 69 additions and 19 deletions
|
@ -1,8 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { injectIntl } from 'react-intl';
|
import { injectIntl } from 'react-intl';
|
||||||
|
import { throttle } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import SvgIcon from 'soapbox/components/svg_icon';
|
import Icon from 'soapbox/components/icon';
|
||||||
|
|
||||||
export default @injectIntl
|
export default @injectIntl
|
||||||
class TimelineQueueButtonHeader extends React.PureComponent {
|
class TimelineQueueButtonHeader extends React.PureComponent {
|
||||||
|
@ -11,25 +12,68 @@ class TimelineQueueButtonHeader extends React.PureComponent {
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
count: PropTypes.number,
|
count: PropTypes.number,
|
||||||
message: PropTypes.object.isRequired,
|
message: PropTypes.object.isRequired,
|
||||||
|
threshold: PropTypes.number,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
count: 0,
|
count: 0,
|
||||||
|
threshold: 400,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
scrolled: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.window = window;
|
||||||
|
this.documentElement = document.scrollingElement || document.documentElement;
|
||||||
|
|
||||||
|
this.attachScrollListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.detachScrollListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
attachScrollListener() {
|
||||||
|
this.window.addEventListener('scroll', this.handleScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
detachScrollListener() {
|
||||||
|
this.window.removeEventListener('scroll', this.handleScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleScroll = throttle(() => {
|
||||||
|
const { scrollTop } = (document.scrollingElement || document.documentElement);
|
||||||
|
const { threshold } = this.props;
|
||||||
|
|
||||||
|
if (scrollTop > threshold) {
|
||||||
|
this.setState({ scrolled: true });
|
||||||
|
} else {
|
||||||
|
this.setState({ scrolled: false });
|
||||||
|
}
|
||||||
|
}, 150, { trailing: true });
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { count, message, onClick, intl } = this.props;
|
const { count, message, onClick, intl } = this.props;
|
||||||
|
const { scrolled } = this.state;
|
||||||
|
|
||||||
|
const visible = count > 0 && scrolled;
|
||||||
|
|
||||||
const classes = classNames('timeline-queue-header', {
|
const classes = classNames('timeline-queue-header', {
|
||||||
'hidden': (count <= 0),
|
'hidden': !visible,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
<a className='timeline-queue-header__btn' onClick={onClick}>
|
<a className='timeline-queue-header__btn' onClick={onClick}>
|
||||||
<SvgIcon src={require('@tabler/icons/icons/arrow-bar-to-up.svg')} />
|
<Icon src={require('@tabler/icons/icons/arrow-bar-to-up.svg')} />
|
||||||
{(count > 0) && intl.formatMessage(message, { count })}
|
{(count > 0) && (
|
||||||
|
<div className='timeline-queue-header__label'>
|
||||||
|
{intl.formatMessage(message, { count })}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -29,9 +29,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.pinned-hosts-picker {
|
.pinned-hosts-picker {
|
||||||
margin-left: 10px;
|
padding: 10px 0 0 10px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
background: var(--foreground-color);
|
||||||
|
border-bottom: 1px solid hsla(var(--primary-text-color_hsl), 0.2);
|
||||||
|
|
||||||
.pinned-host {
|
.pinned-host {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
.timeline-queue-header {
|
.timeline-queue-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-self: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
max-height: 30px;
|
height: 30px;
|
||||||
position: sticky;
|
position: fixed;
|
||||||
top: 60px;
|
top: 60px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
margin-bottom: 8px;
|
|
||||||
background-color: var(--brand-color);
|
background-color: var(--brand-color);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-bottom: 1px solid;
|
|
||||||
border-top: 1px solid;
|
|
||||||
border-color: var(--brand-color--faint);
|
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
transition: max-height 150ms ease;
|
transition: 150ms ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
opacity: 1;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
z-index: 500;
|
z-index: 500;
|
||||||
|
|
||||||
|
.sub-navigation ~ & {
|
||||||
|
top: calc(60px + 41px);
|
||||||
|
}
|
||||||
|
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hidden {
|
&.hidden {
|
||||||
max-height: 0;
|
transform: scaleY(0);
|
||||||
opacity: 0;
|
pointer-events: none;
|
||||||
margin: 0;
|
|
||||||
border: 0;
|
.timeline-queue-header__label {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__btn {
|
&__btn {
|
||||||
|
@ -46,4 +46,8 @@
|
||||||
height: 46px;
|
height: 46px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
transition: 150ms ease;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue