mirror of
https://github.com/mastodon/mastodon.git
synced 2024-08-20 21:08:15 -07:00
Display followed hashtags on the home header
Display the followed hashtags on the home header, allowing for easier access to them. Listed from most recent to oldest, and with 2 dedicated buttons. Co-authored-by: Tiago Peralta <tiagofilipeperalta@tecnico.ulisboa.pt>
This commit is contained in:
parent
1bccba1408
commit
77ec956d92
7 changed files with 584 additions and 8 deletions
|
@ -0,0 +1,258 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<ButtonScrollList /> handles a large number of children correctly 1`] = `
|
||||||
|
<div
|
||||||
|
className="button-scroll-list-container"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Scroll left"
|
||||||
|
className="icon-button column-header__setting-btn"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
MockIcon
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
className="button-scroll-list"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
aria-label="Scroll right"
|
||||||
|
className="icon-button column-header__setting-btn"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
MockIcon
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`<ButtonScrollList /> handles a single child correctly 1`] = `
|
||||||
|
<div
|
||||||
|
className="button-scroll-list-container"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Scroll left"
|
||||||
|
className="icon-button column-header__setting-btn"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
MockIcon
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
className="button-scroll-list"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
aria-label="Scroll right"
|
||||||
|
className="icon-button column-header__setting-btn"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
MockIcon
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`<ButtonScrollList /> renders an empty button scroll list element 1`] = `null`;
|
||||||
|
|
||||||
|
exports[`<ButtonScrollList /> renders the children 1`] = `
|
||||||
|
<div
|
||||||
|
className="button-scroll-list-container"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
aria-label="Scroll left"
|
||||||
|
className="icon-button column-header__setting-btn"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
MockIcon
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
className="button-scroll-list"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
aria-label="Scroll right"
|
||||||
|
className="icon-button column-header__setting-btn"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
MockIcon
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
|
@ -0,0 +1,119 @@
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
|
||||||
|
import { render, screen } from 'mastodon/test_helpers';
|
||||||
|
|
||||||
|
import ButtonScrollList from '../button_scroll_list';
|
||||||
|
|
||||||
|
jest.mock('mastodon/components/icon', () => {
|
||||||
|
return {
|
||||||
|
Icon: () => <div>MockIcon</div>,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('<ButtonScrollList />', () => {
|
||||||
|
it('renders an empty button scroll list element', () => {
|
||||||
|
const children = [];
|
||||||
|
const component = renderer.create(
|
||||||
|
<ButtonScrollList>{children}</ButtonScrollList>,
|
||||||
|
);
|
||||||
|
const tree = component.toJSON();
|
||||||
|
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the children', () => {
|
||||||
|
const children = Array.from({ length: 5 }, (_, i) => (
|
||||||
|
<div key={i} ref={jest.fn()} />
|
||||||
|
));
|
||||||
|
const component = renderer.create(
|
||||||
|
<ButtonScrollList>{children}</ButtonScrollList>,
|
||||||
|
);
|
||||||
|
const tree = component.toJSON();
|
||||||
|
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls left', () => {
|
||||||
|
const children = Array.from({ length: 5 }, (_, i) => (
|
||||||
|
<div key={i} ref={jest.fn()} />
|
||||||
|
));
|
||||||
|
const component = renderer.create(
|
||||||
|
<ButtonScrollList>{children}</ButtonScrollList>,
|
||||||
|
);
|
||||||
|
const instance = component.getInstance();
|
||||||
|
instance.scrollLeft();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls right', () => {
|
||||||
|
const children = Array.from({ length: 5 }, (_, i) => (
|
||||||
|
<div key={i} ref={jest.fn()} />
|
||||||
|
));
|
||||||
|
const component = renderer.create(
|
||||||
|
<ButtonScrollList>{children}</ButtonScrollList>,
|
||||||
|
);
|
||||||
|
const instance = component.getInstance();
|
||||||
|
instance.scrollRight();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls left and right correctly', () => {
|
||||||
|
const children = Array.from({ length: 10 }, (_, i) => (
|
||||||
|
<div key={i}>{i}</div>
|
||||||
|
));
|
||||||
|
const component = renderer.create(
|
||||||
|
<ButtonScrollList>{children}</ButtonScrollList>,
|
||||||
|
);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
let instance = component.getInstance();
|
||||||
|
instance.scrollRight();
|
||||||
|
expect(instance.slide).toBe(1);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
let instance = component.getInstance();
|
||||||
|
instance.scrollLeft();
|
||||||
|
expect(instance.slide).toBe(0);
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles a single child correctly', () => {
|
||||||
|
const children = [<div key={0} ref={jest.fn()} />];
|
||||||
|
const component = renderer.create(
|
||||||
|
<ButtonScrollList>{children}</ButtonScrollList>,
|
||||||
|
);
|
||||||
|
const tree = component.toJSON();
|
||||||
|
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles a large number of children correctly', () => {
|
||||||
|
const children = Array.from({ length: 50 }, (_, i) => (
|
||||||
|
<div key={i} ref={jest.fn()} />
|
||||||
|
));
|
||||||
|
const component = renderer.create(
|
||||||
|
<ButtonScrollList>{children}</ButtonScrollList>,
|
||||||
|
);
|
||||||
|
const tree = component.toJSON();
|
||||||
|
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks if scroll buttons are accessible', () => {
|
||||||
|
const children = Array.from({ length: 5 }, (_, i) => (
|
||||||
|
<div key={i} ref={jest.fn()} />
|
||||||
|
));
|
||||||
|
render(<ButtonScrollList>{children}</ButtonScrollList>);
|
||||||
|
|
||||||
|
const leftButton = screen.getByRole('button', { name: /scroll left/i });
|
||||||
|
const rightButton = screen.getByRole('button', { name: /scroll right/i });
|
||||||
|
|
||||||
|
expect(leftButton).toBeTruthy();
|
||||||
|
expect(rightButton).toBeTruthy();
|
||||||
|
|
||||||
|
leftButton.focus();
|
||||||
|
expect(document.activeElement).toBe(leftButton);
|
||||||
|
|
||||||
|
rightButton.focus();
|
||||||
|
expect(document.activeElement).toBe(rightButton);
|
||||||
|
});
|
||||||
|
});
|
87
app/javascript/mastodon/components/button_scroll_list.jsx
Normal file
87
app/javascript/mastodon/components/button_scroll_list.jsx
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
||||||
|
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||||
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
|
class ButtonScrollList extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
children: PropTypes.node.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.scrollRef = React.createRef();
|
||||||
|
this.slide = 0;
|
||||||
|
this.childrenLength = React.Children.count(props.children);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.scrollRef && this.scrollRef.current) {
|
||||||
|
const container = this.scrollRef.current;
|
||||||
|
container.scrollTo({ left: 0, behavior: 'auto' });
|
||||||
|
this.slide = 0;
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollLeft = () => {
|
||||||
|
if (this.scrollRef && this.scrollRef.current) {
|
||||||
|
this.scrollRef.current.scrollBy({ left: -200, behavior: 'smooth' });
|
||||||
|
this.slide = Math.max(0, this.slide - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scrollRight = () => {
|
||||||
|
if (this.scrollRef && this.scrollRef.current) {
|
||||||
|
const { children } = this.props;
|
||||||
|
const container = this.scrollRef.current;
|
||||||
|
const maxScrollLeft = container.scrollWidth - container.clientWidth;
|
||||||
|
|
||||||
|
if (container.scrollLeft < maxScrollLeft) {
|
||||||
|
container.scrollBy({ left: 200, behavior: 'smooth' });
|
||||||
|
this.slide = Math.min(
|
||||||
|
React.Children.count(children) - 1,
|
||||||
|
this.slide + 1,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { children } = this.props;
|
||||||
|
|
||||||
|
if (React.Children.count(children) === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='button-scroll-list-container'>
|
||||||
|
<button
|
||||||
|
className='icon-button column-header__setting-btn'
|
||||||
|
aria-label='Scroll left'
|
||||||
|
onClick={this.scrollLeft}
|
||||||
|
>
|
||||||
|
<Icon id='chevron-left' icon={ChevronLeftIcon} />
|
||||||
|
</button>
|
||||||
|
<div className='button-scroll-list' ref={this.scrollRef}>
|
||||||
|
{React.Children.map(children, (child, index) => (
|
||||||
|
<div key={index}>{child}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className='icon-button column-header__setting-btn'
|
||||||
|
aria-label='Scroll right'
|
||||||
|
onClick={this.scrollRight}
|
||||||
|
>
|
||||||
|
<Icon id='chevron-right' icon={ChevronRightIcon} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ButtonScrollList;
|
|
@ -10,6 +10,7 @@ import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
||||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
|
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
|
||||||
|
import FollowedTagsList from 'mastodon/components/followed_tags_list';
|
||||||
import type { IconProp } from 'mastodon/components/icon';
|
import type { IconProp } from 'mastodon/components/icon';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
|
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
|
||||||
|
@ -255,7 +256,7 @@ export const ColumnHeader: React.FC<Props> = ({
|
||||||
<>
|
<>
|
||||||
{backButton}
|
{backButton}
|
||||||
|
|
||||||
<button onClick={handleTitleClick} className='column-header__title'>
|
<button onClick={handleTitleClick} className='column-header__title' style={{ overflow: 'visible', paddingRight: '15px' }}>
|
||||||
{!backButton && (
|
{!backButton && (
|
||||||
<Icon
|
<Icon
|
||||||
id={icon}
|
id={icon}
|
||||||
|
@ -268,6 +269,10 @@ export const ColumnHeader: React.FC<Props> = ({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{ icon === 'home' ? (
|
||||||
|
<FollowedTagsList />
|
||||||
|
) : null }
|
||||||
|
|
||||||
{!hasTitle && backButton}
|
{!hasTitle && backButton}
|
||||||
|
|
||||||
<div className='column-header__buttons'>
|
<div className='column-header__buttons'>
|
||||||
|
|
67
app/javascript/mastodon/components/followed_tags_list.jsx
Normal file
67
app/javascript/mastodon/components/followed_tags_list.jsx
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import { injectIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
|
import {
|
||||||
|
expandFollowedHashtags,
|
||||||
|
fetchFollowedHashtags,
|
||||||
|
} from 'mastodon/actions/tags';
|
||||||
|
import ButtonScrollList from 'mastodon/components/button_scroll_list';
|
||||||
|
import { Hashtag } from 'mastodon/components/hashtag';
|
||||||
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
hashtags: state.getIn(['followed_tags', 'items']),
|
||||||
|
isLoading: state.getIn(['followed_tags', 'isLoading'], true),
|
||||||
|
hasMore: !!state.getIn(['followed_tags', 'next']),
|
||||||
|
});
|
||||||
|
|
||||||
|
class FollowedTagsList extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
hashtags: ImmutablePropTypes.list.isRequired,
|
||||||
|
isLoading: PropTypes.bool.isRequired,
|
||||||
|
hasMore: PropTypes.bool.isRequired,
|
||||||
|
...WithRouterPropTypes,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.dispatch(fetchFollowedHashtags());
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLoadMore = debounce(
|
||||||
|
() => {
|
||||||
|
this.props.dispatch(expandFollowedHashtags());
|
||||||
|
},
|
||||||
|
300,
|
||||||
|
{ leading: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { hashtags } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='followed-tags-list'>
|
||||||
|
<ButtonScrollList>
|
||||||
|
{hashtags.map((hashtag) => (
|
||||||
|
<div className='hashtag-wrapper' key={hashtag.get('name')}>
|
||||||
|
<Hashtag
|
||||||
|
name={hashtag.get('name')}
|
||||||
|
showSkeleton={false}
|
||||||
|
to={`/tags/${hashtag.get('name')}`}
|
||||||
|
withGraph={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</ButtonScrollList>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(injectIntl(FollowedTagsList));
|
|
@ -85,6 +85,7 @@ export interface HashtagProps {
|
||||||
description?: React.ReactNode;
|
description?: React.ReactNode;
|
||||||
history?: number[];
|
history?: number[];
|
||||||
name: string;
|
name: string;
|
||||||
|
showSkeleton?: boolean;
|
||||||
people: number;
|
people: number;
|
||||||
to: string;
|
to: string;
|
||||||
uses?: number;
|
uses?: number;
|
||||||
|
@ -93,6 +94,7 @@ export interface HashtagProps {
|
||||||
|
|
||||||
export const Hashtag: React.FC<HashtagProps> = ({
|
export const Hashtag: React.FC<HashtagProps> = ({
|
||||||
name,
|
name,
|
||||||
|
showSkeleton = true,
|
||||||
to,
|
to,
|
||||||
people,
|
people,
|
||||||
uses,
|
uses,
|
||||||
|
@ -113,13 +115,15 @@ export const Hashtag: React.FC<HashtagProps> = ({
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{description ? (
|
{showSkeleton ? (
|
||||||
<span>{description}</span>
|
description ? (
|
||||||
) : typeof people !== 'undefined' ? (
|
<span>{description}</span>
|
||||||
<ShortNumber value={people} renderer={accountsCountRenderer} />
|
) : typeof people !== 'undefined' ? (
|
||||||
) : (
|
<ShortNumber value={people} renderer={accountsCountRenderer} />
|
||||||
<Skeleton width={100} />
|
) : (
|
||||||
)}
|
<Skeleton width={100} />
|
||||||
|
)
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{typeof uses !== 'undefined' && (
|
{typeof uses !== 'undefined' && (
|
||||||
|
|
|
@ -3571,6 +3571,42 @@ $ui-header-logo-wordmark-width: 99px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-scroll-list-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-scroll-list {
|
||||||
|
display: flex;
|
||||||
|
overflow-x: auto;
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
flex-grow: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-scroll-list::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.followed-tags-list {
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hashtag-wrapper {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
.column-back-button {
|
.column-back-button {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
Loading…
Reference in a new issue