Use a context to propagate column-related Props, and remove forceUpdate
usage (#27548)
This commit is contained in:
parent
3ca974e101
commit
537442853f
20 changed files with 230 additions and 188 deletions
|
@ -1,65 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
export class ColumnBackButton extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
multiColumn: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
const { onClick, history } = this.props;
|
||||
|
||||
if (onClick) {
|
||||
onClick();
|
||||
} else if (history.location?.state?.fromMastodon) {
|
||||
history.goBack();
|
||||
} else {
|
||||
history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { multiColumn } = this.props;
|
||||
|
||||
const component = (
|
||||
<button onClick={this.handleClick} className='column-back-button'>
|
||||
<Icon id='chevron-left' icon={ArrowBackIcon} className='column-back-button__icon' />
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
</button>
|
||||
);
|
||||
|
||||
if (multiColumn) {
|
||||
return component;
|
||||
} else {
|
||||
// The portal container and the component may be rendered to the DOM in
|
||||
// the same React render pass, so the container might not be available at
|
||||
// the time `render()` is called.
|
||||
const container = document.getElementById('tabs-bar__portal');
|
||||
if (container === null) {
|
||||
// The container wasn't available, force a re-render so that the
|
||||
// component can eventually be inserted in the container and not scroll
|
||||
// with the rest of the area.
|
||||
this.forceUpdate();
|
||||
return component;
|
||||
} else {
|
||||
return createPortal(component, container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(ColumnBackButton);
|
70
app/javascript/mastodon/components/column_back_button.tsx
Normal file
70
app/javascript/mastodon/components/column_back_button.tsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
|
||||
|
||||
import { useAppHistory } from './router';
|
||||
|
||||
type OnClickCallback = () => void;
|
||||
|
||||
function useHandleClick(onClick?: OnClickCallback) {
|
||||
const history = useAppHistory();
|
||||
|
||||
return useCallback(() => {
|
||||
if (onClick) {
|
||||
onClick();
|
||||
} else if (history.location.state?.fromMastodon) {
|
||||
history.goBack();
|
||||
} else {
|
||||
history.push('/');
|
||||
}
|
||||
}, [history, onClick]);
|
||||
}
|
||||
|
||||
export const ColumnBackButton: React.FC<{ onClick: OnClickCallback }> = ({
|
||||
onClick,
|
||||
}) => {
|
||||
const handleClick = useHandleClick(onClick);
|
||||
|
||||
const component = (
|
||||
<button onClick={handleClick} className='column-back-button'>
|
||||
<Icon
|
||||
id='chevron-left'
|
||||
icon={ArrowBackIcon}
|
||||
className='column-back-button__icon'
|
||||
/>
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
</button>
|
||||
);
|
||||
|
||||
return <ButtonInTabsBar>{component}</ButtonInTabsBar>;
|
||||
};
|
||||
|
||||
export const ColumnBackButtonSlim: React.FC<{ onClick: OnClickCallback }> = ({
|
||||
onClick,
|
||||
}) => {
|
||||
const handleClick = useHandleClick(onClick);
|
||||
|
||||
return (
|
||||
<div className='column-back-button--slim'>
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
|
||||
<div
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
onClick={handleClick}
|
||||
className='column-back-button column-back-button--slim-button'
|
||||
>
|
||||
<Icon
|
||||
id='chevron-left'
|
||||
icon={ArrowBackIcon}
|
||||
className='column-back-button__icon'
|
||||
/>
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { ColumnBackButton } from './column_back_button';
|
||||
|
||||
export default class ColumnBackButtonSlim extends ColumnBackButton {
|
||||
render () {
|
||||
return (
|
||||
<div className='column-back-button--slim'>
|
||||
<div role='button' tabIndex={0} onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
|
||||
<Icon id='chevron-left' icon={ArrowBackIcon} className='column-back-button__icon' />
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
|
||||
|
@ -15,6 +14,7 @@ import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/
|
|||
import { ReactComponent as TuneIcon } from '@material-symbols/svg-600/outlined/tune.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -203,22 +203,12 @@ class ColumnHeader extends PureComponent {
|
|||
</div>
|
||||
);
|
||||
|
||||
if (multiColumn || placeholder) {
|
||||
if (placeholder) {
|
||||
return component;
|
||||
} else {
|
||||
// The portal container and the component may be rendered to the DOM in
|
||||
// the same React render pass, so the container might not be available at
|
||||
// the time `render()` is called.
|
||||
const container = document.getElementById('tabs-bar__portal');
|
||||
if (container === null) {
|
||||
// The container wasn't available, force a re-render so that the
|
||||
// component can eventually be inserted in the container and not scroll
|
||||
// with the rest of the area.
|
||||
this.forceUpdate();
|
||||
return component;
|
||||
} else {
|
||||
return createPortal(component, container);
|
||||
}
|
||||
return (<ButtonInTabsBar>
|
||||
{component}
|
||||
</ButtonInTabsBar>);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { PropsWithChildren } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { Router as OriginalRouter } from 'react-router';
|
||||
import { Router as OriginalRouter, useHistory } from 'react-router';
|
||||
|
||||
import type {
|
||||
LocationDescriptor,
|
||||
|
@ -16,18 +16,23 @@ interface MastodonLocationState {
|
|||
fromMastodon?: boolean;
|
||||
mastodonModalKey?: string;
|
||||
}
|
||||
type HistoryPath = Path | LocationDescriptor<MastodonLocationState>;
|
||||
|
||||
const browserHistory = createBrowserHistory<
|
||||
MastodonLocationState | undefined
|
||||
>();
|
||||
type LocationState = MastodonLocationState | null | undefined;
|
||||
|
||||
type HistoryPath = Path | LocationDescriptor<LocationState>;
|
||||
|
||||
const browserHistory = createBrowserHistory<LocationState>();
|
||||
const originalPush = browserHistory.push.bind(browserHistory);
|
||||
const originalReplace = browserHistory.replace.bind(browserHistory);
|
||||
|
||||
export function useAppHistory() {
|
||||
return useHistory<LocationState>();
|
||||
}
|
||||
|
||||
function normalizePath(
|
||||
path: HistoryPath,
|
||||
state?: MastodonLocationState,
|
||||
): LocationDescriptorObject<MastodonLocationState> {
|
||||
state?: LocationState,
|
||||
): LocationDescriptorObject<LocationState> {
|
||||
const location = typeof path === 'string' ? { pathname: path } : { ...path };
|
||||
|
||||
if (location.state === undefined && state !== undefined) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue