mirror of
https://github.com/mastodon/mastodon.git
synced 2024-12-22 23:20:44 +01:00
Refactor <Column>
to TypeScript (#33081)
This commit is contained in:
parent
752d49eefe
commit
346a27b6fc
11 changed files with 67 additions and 84 deletions
|
@ -1,72 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { PureComponent } from 'react';
|
|
||||||
|
|
||||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
|
||||||
|
|
||||||
import { scrollTop } from '../scroll';
|
|
||||||
|
|
||||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
|
||||||
|
|
||||||
export default class Column extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
label: PropTypes.string,
|
|
||||||
bindToDocument: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
scrollTop () {
|
|
||||||
let scrollable = null;
|
|
||||||
|
|
||||||
if (this.props.bindToDocument) {
|
|
||||||
scrollable = document.scrollingElement;
|
|
||||||
} else {
|
|
||||||
scrollable = this.node.querySelector('.scrollable');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!scrollable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._interruptScrollAnimation = scrollTop(scrollable);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleWheel = () => {
|
|
||||||
if (typeof this._interruptScrollAnimation !== 'function') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._interruptScrollAnimation();
|
|
||||||
};
|
|
||||||
|
|
||||||
setRef = c => {
|
|
||||||
this.node = c;
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
if (this.props.bindToDocument) {
|
|
||||||
document.addEventListener('wheel', this.handleWheel, listenerOptions);
|
|
||||||
} else {
|
|
||||||
this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
if (this.props.bindToDocument) {
|
|
||||||
document.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
|
||||||
} else {
|
|
||||||
this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { label, children } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div role='region' aria-label={label} className='column' ref={this.setRef}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
52
app/javascript/mastodon/components/column.tsx
Normal file
52
app/javascript/mastodon/components/column.tsx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { forwardRef, useRef, useImperativeHandle } from 'react';
|
||||||
|
import type { Ref } from 'react';
|
||||||
|
|
||||||
|
import { scrollTop } from 'mastodon/scroll';
|
||||||
|
|
||||||
|
export interface ColumnRef {
|
||||||
|
scrollTop: () => void;
|
||||||
|
node: HTMLDivElement | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ColumnProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
label?: string;
|
||||||
|
bindToDocument?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Column = forwardRef<ColumnRef, ColumnProps>(
|
||||||
|
({ children, label, bindToDocument }, ref: Ref<ColumnRef>) => {
|
||||||
|
const nodeRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
node: nodeRef.current,
|
||||||
|
|
||||||
|
scrollTop() {
|
||||||
|
let scrollable = null;
|
||||||
|
|
||||||
|
if (bindToDocument) {
|
||||||
|
scrollable = document.scrollingElement;
|
||||||
|
} else {
|
||||||
|
scrollable = nodeRef.current?.querySelector('.scrollable');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scrollable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTop(scrollable);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div role='region' aria-label={label} className='column' ref={nodeRef}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Column.displayName = 'Column';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export default Column;
|
|
@ -15,7 +15,8 @@ import {
|
||||||
changeColumnParams,
|
changeColumnParams,
|
||||||
} from 'mastodon/actions/columns';
|
} from 'mastodon/actions/columns';
|
||||||
import { fetchDirectory, expandDirectory } from 'mastodon/actions/directory';
|
import { fetchDirectory, expandDirectory } from 'mastodon/actions/directory';
|
||||||
import Column from 'mastodon/components/column';
|
import { Column } from 'mastodon/components/column';
|
||||||
|
import type { ColumnRef } from 'mastodon/components/column';
|
||||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||||
import { LoadMore } from 'mastodon/components/load_more';
|
import { LoadMore } from 'mastodon/components/load_more';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
|
@ -49,7 +50,7 @@ export const Directory: React.FC<{
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const column = useRef<Column>(null);
|
const column = useRef<ColumnRef>(null);
|
||||||
|
|
||||||
const [orderParam, setOrderParam] = useSearchParam('order');
|
const [orderParam, setOrderParam] = useSearchParam('order');
|
||||||
const [localParam, setLocalParam] = useSearchParam('local');
|
const [localParam, setLocalParam] = useSearchParam('local');
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
import ExploreIcon from '@/material-icons/400-24px/explore.svg?react';
|
||||||
import { expandLinkTimeline } from 'mastodon/actions/timelines';
|
import { expandLinkTimeline } from 'mastodon/actions/timelines';
|
||||||
import Column from 'mastodon/components/column';
|
import { Column } from 'mastodon/components/column';
|
||||||
|
import type { ColumnRef } from 'mastodon/components/column';
|
||||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||||
import StatusListContainer from 'mastodon/features/ui/containers/status_list_container';
|
import StatusListContainer from 'mastodon/features/ui/containers/status_list_container';
|
||||||
import type { Card } from 'mastodon/models/status';
|
import type { Card } from 'mastodon/models/status';
|
||||||
|
@ -17,7 +18,7 @@ export const LinkTimeline: React.FC<{
|
||||||
const { url } = useParams<{ url: string }>();
|
const { url } = useParams<{ url: string }>();
|
||||||
const decodedUrl = url ? decodeURIComponent(url) : undefined;
|
const decodedUrl = url ? decodeURIComponent(url) : undefined;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const columnRef = useRef<Column>(null);
|
const columnRef = useRef<ColumnRef>(null);
|
||||||
const firstStatusId = useAppSelector((state) =>
|
const firstStatusId = useAppSelector((state) =>
|
||||||
decodedUrl
|
decodedUrl
|
||||||
? // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
? // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
|
|
|
@ -11,7 +11,7 @@ import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
|
import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
|
||||||
import { fetchLists } from 'mastodon/actions/lists';
|
import { fetchLists } from 'mastodon/actions/lists';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import Column from 'mastodon/components/column';
|
import { Column } from 'mastodon/components/column';
|
||||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
import Column from 'mastodon/components/column';
|
import { Column } from 'mastodon/components/column';
|
||||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||||
import { ColumnSearchHeader } from 'mastodon/components/column_search_header';
|
import { ColumnSearchHeader } from 'mastodon/components/column_search_header';
|
||||||
import { FollowersCounter } from 'mastodon/components/counters';
|
import { FollowersCounter } from 'mastodon/components/counters';
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { fetchList } from 'mastodon/actions/lists';
|
||||||
import { createList, updateList } from 'mastodon/actions/lists_typed';
|
import { createList, updateList } from 'mastodon/actions/lists_typed';
|
||||||
import { apiGetAccounts } from 'mastodon/api/lists';
|
import { apiGetAccounts } from 'mastodon/api/lists';
|
||||||
import type { RepliesPolicyType } from 'mastodon/api_types/lists';
|
import type { RepliesPolicyType } from 'mastodon/api_types/lists';
|
||||||
import Column from 'mastodon/components/column';
|
import { Column } from 'mastodon/components/column';
|
||||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||||
|
|
|
@ -36,7 +36,8 @@ import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||||
|
|
||||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||||
import { submitMarkers } from '../../actions/markers';
|
import { submitMarkers } from '../../actions/markers';
|
||||||
import Column from '../../components/column';
|
import { Column } from '../../components/column';
|
||||||
|
import type { ColumnRef } from '../../components/column';
|
||||||
import { ColumnHeader } from '../../components/column_header';
|
import { ColumnHeader } from '../../components/column_header';
|
||||||
import { LoadGap } from '../../components/load_gap';
|
import { LoadGap } from '../../components/load_gap';
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
|
@ -96,7 +97,7 @@ export const Notifications: React.FC<{
|
||||||
selectNeedsNotificationPermission,
|
selectNeedsNotificationPermission,
|
||||||
);
|
);
|
||||||
|
|
||||||
const columnRef = useRef<Column>(null);
|
const columnRef = useRef<ColumnRef>(null);
|
||||||
|
|
||||||
const selectChild = useCallback((index: number, alignTop: boolean) => {
|
const selectChild = useCallback((index: number, alignTop: boolean) => {
|
||||||
const container = columnRef.current?.node as HTMLElement | undefined;
|
const container = columnRef.current?.node as HTMLElement | undefined;
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { fetchSuggestions } from 'mastodon/actions/suggestions';
|
||||||
import { markAsPartial } from 'mastodon/actions/timelines';
|
import { markAsPartial } from 'mastodon/actions/timelines';
|
||||||
import { apiRequest } from 'mastodon/api';
|
import { apiRequest } from 'mastodon/api';
|
||||||
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
|
||||||
import Column from 'mastodon/components/column';
|
import { Column } from 'mastodon/components/column';
|
||||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||||
import { ColumnSearchHeader } from 'mastodon/components/column_search_header';
|
import { ColumnSearchHeader } from 'mastodon/components/column_search_header';
|
||||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||||
|
|
|
@ -13,7 +13,7 @@ import EditIcon from '@/material-icons/400-24px/edit.svg?react';
|
||||||
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
|
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
|
||||||
import { updateAccount } from 'mastodon/actions/accounts';
|
import { updateAccount } from 'mastodon/actions/accounts';
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
import Column from 'mastodon/components/column';
|
import { Column } from 'mastodon/components/column';
|
||||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Column from 'mastodon/components/column';
|
import { Column } from 'mastodon/components/column';
|
||||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||||
import type { Props as ColumnHeaderProps } from 'mastodon/components/column_header';
|
import type { Props as ColumnHeaderProps } from 'mastodon/components/column_header';
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue