Transition: Allow static children

This commit is contained in:
Alexander Zinchuk 2022-02-25 22:52:19 +02:00
parent 59c402f0e3
commit 8b60cef7c2
16 changed files with 209 additions and 222 deletions

View File

@ -18,9 +18,7 @@ const MessageOutgoingStatus: FC<OwnProps> = ({ status }) => {
return ( return (
<div className="MessageOutgoingStatus"> <div className="MessageOutgoingStatus">
<Transition name="reveal" activeKey={Keys[status]}> <Transition name="reveal" activeKey={Keys[status]}>
{() => ( <i className={`icon-message-${status}`} />
<i className={`icon-message-${status}`} />
)}
</Transition> </Transition>
</div> </div>
); );

View File

@ -191,7 +191,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
<div className="photo-wrapper"> <div className="photo-wrapper">
{renderPhotoTabs()} {renderPhotoTabs()}
<Transition activeKey={currentPhotoIndex} name={slideAnimation} className="profile-slide-container"> <Transition activeKey={currentPhotoIndex} name={slideAnimation} className="profile-slide-container">
{renderPhoto} {renderPhoto()}
</Transition> </Transition>
{!isFirst && ( {!isFirst && (

View File

@ -27,7 +27,7 @@ const ConnectionStatusOverlay: FC<OwnProps> = ({
<Spinner color="black" /> <Spinner color="black" />
<div className="state-text"> <div className="state-text">
<Transition activeKey={connectionStatus} name="slide-fade"> <Transition activeKey={connectionStatus} name="slide-fade">
{() => connectionStatusText} {connectionStatusText}
</Transition> </Transition>
</div> </div>
<Button <Button

View File

@ -61,7 +61,7 @@ const Badge: FC<OwnProps> = ({ chat, isPinned, isMuted }) => {
return ( return (
<ShowTransition isCustom className="Badge-transition" isOpen={isShown}> <ShowTransition isCustom className="Badge-transition" isOpen={isShown}>
{renderContent} {renderContent()}
</ShowTransition> </ShowTransition>
); );
}; };

View File

@ -331,13 +331,11 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
isCustom isCustom
className="connection-state-wrapper" className="connection-state-wrapper"
> >
{() => ( <ConnectionStatusOverlay
<ConnectionStatusOverlay connectionStatus={connectionStatus}
connectionStatus={connectionStatus} connectionStatusText={connectionStatusText!}
connectionStatusText={connectionStatusText!} onClick={toggleConnectionStatus}
onClick={toggleConnectionStatus} />
/>
)}
</ShowTransition> </ShowTransition>
</div> </div>
</div> </div>

View File

@ -90,7 +90,7 @@ const LeftSearch: FC<OwnProps & StateProps> = ({
renderCount={TRANSITION_RENDER_COUNT} renderCount={TRANSITION_RENDER_COUNT}
activeKey={currentContent} activeKey={currentContent}
> >
{() => { {(() => {
switch (currentContent) { switch (currentContent) {
case GlobalSearchContent.ChatList: case GlobalSearchContent.ChatList:
if (chatId) { if (chatId) {
@ -136,7 +136,7 @@ const LeftSearch: FC<OwnProps & StateProps> = ({
default: default:
return undefined; return undefined;
} }
}} })()}
</Transition> </Transition>
</div> </div>
); );

View File

@ -461,96 +461,92 @@ const MediaViewer: FC<StateProps> = ({
className={isZoomed ? 'zoomed' : ''} className={isZoomed ? 'zoomed' : ''}
isOpen={isOpen} isOpen={isOpen}
> >
{() => ( <div className="media-viewer-head" dir={lang.isRtl ? 'rtl' : undefined}>
<> {IS_SINGLE_COLUMN_LAYOUT && (
<div className="media-viewer-head" dir={lang.isRtl ? 'rtl' : undefined}> <Button
{IS_SINGLE_COLUMN_LAYOUT && ( className="media-viewer-close"
<Button round
className="media-viewer-close" size="smaller"
round color="translucent-white"
size="smaller" ariaLabel={lang('Close')}
color="translucent-white" onClick={close}
ariaLabel={lang('Close')}
onClick={close}
>
<i className="icon-close" />
</Button>
)}
<Transition activeKey={animationKey.current!} name={headerAnimation}>
{renderSenderInfo}
</Transition>
<MediaViewerActions
mediaData={fullMediaBlobUrl || previewBlobUrl}
isVideo={isVideo}
isZoomed={isZoomed}
message={message}
fileName={fileName}
onCloseMediaViewer={close}
onForward={handleForward}
onZoomToggle={handleZoomToggle}
isAvatar={isAvatar}
/>
</div>
<PanZoom
noWrap={!canPanZoomWrap}
canPan={isZoomed}
panDeltaX={panDelta.x}
panDeltaY={panDelta.y}
zoomLevel={zoomLevel}
onPan={handlePan}
> >
<SlideTransition <i className="icon-close" />
activeKey={selectedMediaMessageIndex} </Button>
name={slideAnimation} )}
> <Transition activeKey={animationKey.current!} name={headerAnimation}>
{(isActive: boolean) => ( {renderSenderInfo()}
<MediaViewerSlides </Transition>
messageId={messageId} <MediaViewerActions
getMessageId={getMessageId} mediaData={fullMediaBlobUrl || previewBlobUrl}
chatId={chatId} isVideo={isVideo}
isPhoto={isPhoto} isZoomed={isZoomed}
isGif={isGif} message={message}
threadId={threadId} fileName={fileName}
avatarOwnerId={avatarOwner && avatarOwner.id} onCloseMediaViewer={close}
profilePhotoIndex={profilePhotoIndex} onForward={handleForward}
origin={origin} onZoomToggle={handleZoomToggle}
isOpen={isOpen} isAvatar={isAvatar}
hasFooter={hasFooter} />
isZoomed={isZoomed} </div>
isActive={isActive} <PanZoom
isVideo={isVideo} noWrap={!canPanZoomWrap}
animationLevel={animationLevel} canPan={isZoomed}
onClose={close} panDeltaX={panDelta.x}
selectMessage={selectMessage} panDeltaY={panDelta.y}
onFooterClick={handleFooterClick} zoomLevel={zoomLevel}
/> onPan={handlePan}
)} >
</SlideTransition> <SlideTransition
</PanZoom> activeKey={selectedMediaMessageIndex}
{!isFirst && !IS_TOUCH_ENV && ( name={slideAnimation}
<button >
type="button" {(isActive: boolean) => (
className={`navigation prev ${isVideo && !isGif && 'inline'}`} <MediaViewerSlides
aria-label={lang('AccDescrPrevious')} messageId={messageId}
dir={lang.isRtl ? 'rtl' : undefined} getMessageId={getMessageId}
onClick={() => selectMessage(previousMessageId)} chatId={chatId}
isPhoto={isPhoto}
isGif={isGif}
threadId={threadId}
avatarOwnerId={avatarOwner && avatarOwner.id}
profilePhotoIndex={profilePhotoIndex}
origin={origin}
isOpen={isOpen}
hasFooter={hasFooter}
isZoomed={isZoomed}
isActive={isActive}
isVideo={isVideo}
animationLevel={animationLevel}
onClose={close}
selectMessage={selectMessage}
onFooterClick={handleFooterClick}
/> />
)} )}
{!isLast && !IS_TOUCH_ENV && ( </SlideTransition>
<button </PanZoom>
type="button" {!isFirst && !IS_TOUCH_ENV && (
className={`navigation next ${isVideo && !isGif && 'inline'}`} <button
aria-label={lang('Next')} type="button"
dir={lang.isRtl ? 'rtl' : undefined} className={`navigation prev ${isVideo && !isGif && 'inline'}`}
onClick={() => selectMessage(nextMessageId)} aria-label={lang('AccDescrPrevious')}
/> dir={lang.isRtl ? 'rtl' : undefined}
)} onClick={() => selectMessage(previousMessageId)}
<ZoomControls />
isShown={isZoomed}
onChangeZoom={handleZoomValue}
/>
</>
)} )}
{!isLast && !IS_TOUCH_ENV && (
<button
type="button"
className={`navigation next ${isVideo && !isGif && 'inline'}`}
aria-label={lang('Next')}
dir={lang.isRtl ? 'rtl' : undefined}
onClick={() => selectMessage(nextMessageId)}
/>
)}
<ZoomControls
isShown={isZoomed}
onChangeZoom={handleZoomValue}
/>
</ShowTransition> </ShowTransition>
); );
}; };

View File

@ -2,9 +2,9 @@ import React, { FC } from '../../lib/teact/teact';
import { IS_TOUCH_ENV } from '../../util/environment'; import { IS_TOUCH_ENV } from '../../util/environment';
import Transition, { TransitionProps } from '../ui/Transition'; import Transition, { ChildrenFn, TransitionProps } from '../ui/Transition';
const SlideTransition: FC<TransitionProps> = ({ children, ...props }) => { const SlideTransition: FC<TransitionProps & { children: ChildrenFn }> = ({ children, ...props }) => {
if (IS_TOUCH_ENV) return children(true, true, 1); if (IS_TOUCH_ENV) return children(true, true, 1);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return <Transition {...props}>{children}</Transition>; return <Transition {...props}>{children}</Transition>;

View File

@ -397,111 +397,107 @@ const MiddleColumn: FC<StateProps> = ({
cleanupExceptionKey={cleanupExceptionKey} cleanupExceptionKey={cleanupExceptionKey}
onStop={handleSlideStop} onStop={handleSlideStop}
> >
{() => ( <MessageList
<> key={`${renderingChatId}-${renderingThreadId}-${renderingMessageListType}`}
<MessageList chatId={renderingChatId}
key={`${renderingChatId}-${renderingThreadId}-${renderingMessageListType}`} threadId={renderingThreadId}
type={renderingMessageListType}
canPost={renderingCanPost}
hasTools={renderingHasTools}
onFabToggle={setIsFabShown}
onNotchToggle={setIsNotchShown}
isReady={isReady}
withBottomShift={withMessageListBottomShift}
/>
<div className={footerClassName}>
{renderingCanPost && (
<Composer
chatId={renderingChatId} chatId={renderingChatId}
threadId={renderingThreadId} threadId={renderingThreadId}
type={renderingMessageListType} messageListType={renderingMessageListType}
canPost={renderingCanPost} dropAreaState={dropAreaState}
hasTools={renderingHasTools} onDropHide={handleHideDropArea}
onFabToggle={setIsFabShown}
onNotchToggle={setIsNotchShown}
isReady={isReady} isReady={isReady}
withBottomShift={withMessageListBottomShift}
/> />
<div className={footerClassName}> )}
{renderingCanPost && ( {isPinnedMessageList && (
<Composer <div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}>
chatId={renderingChatId} <Button
threadId={renderingThreadId} size="tiny"
messageListType={renderingMessageListType} fluid
dropAreaState={dropAreaState} color="secondary"
onDropHide={handleHideDropArea} className="unpin-all-button"
isReady={isReady} onClick={handleOpenUnpinModal}
/> >
)} <i className="icon-unpin" />
{isPinnedMessageList && ( <span>{lang('Chat.Pinned.UnpinAll', pinnedMessagesCount, 'i')}</span>
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}> </Button>
<Button
size="tiny"
fluid
color="secondary"
className="unpin-all-button"
onClick={handleOpenUnpinModal}
>
<i className="icon-unpin" />
<span>{lang('Chat.Pinned.UnpinAll', pinnedMessagesCount, 'i')}</span>
</Button>
</div>
)}
{isMessagingDisabled && (
<div className={messagingDisabledClassName}>
<div className="messaging-disabled-inner">
<span>
{messageSendingRestrictionReason}
</span>
</div>
</div>
)}
{IS_SINGLE_COLUMN_LAYOUT && renderingCanSubscribe && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}>
<Button
size="tiny"
fluid
ripple
className="join-subscribe-button"
onClick={handleSubscribeClick}
>
{lang(renderingIsChannel ? 'ProfileJoinChannel' : 'ProfileJoinGroup')}
</Button>
</div>
)}
{IS_SINGLE_COLUMN_LAYOUT && renderingCanStartBot && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}>
<Button
size="tiny"
fluid
ripple
className="join-subscribe-button"
onClick={handleStartBot}
>
{lang('BotStart')}
</Button>
</div>
)}
{IS_SINGLE_COLUMN_LAYOUT && renderingCanRestartBot && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}>
<Button
size="tiny"
fluid
ripple
className="join-subscribe-button"
onClick={handleRestartBot}
>
{lang('BotRestart')}
</Button>
</div>
)}
<MessageSelectToolbar
messageListType={renderingMessageListType}
isActive={isSelectModeActive}
canPost={renderingCanPost}
/>
<PaymentModal
isOpen={Boolean(isPaymentModalOpen)}
onClose={closePaymentModal}
/>
<ReceiptModal
isOpen={Boolean(isReceiptModalOpen)}
onClose={clearReceipt}
/>
<SeenByModal isOpen={isSeenByModalOpen} />
<ReactorListModal isOpen={isReactorListModalOpen} />
</div> </div>
</> )}
)} {isMessagingDisabled && (
<div className={messagingDisabledClassName}>
<div className="messaging-disabled-inner">
<span>
{messageSendingRestrictionReason}
</span>
</div>
</div>
)}
{IS_SINGLE_COLUMN_LAYOUT && renderingCanSubscribe && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}>
<Button
size="tiny"
fluid
ripple
className="join-subscribe-button"
onClick={handleSubscribeClick}
>
{lang(renderingIsChannel ? 'ProfileJoinChannel' : 'ProfileJoinGroup')}
</Button>
</div>
)}
{IS_SINGLE_COLUMN_LAYOUT && renderingCanStartBot && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}>
<Button
size="tiny"
fluid
ripple
className="join-subscribe-button"
onClick={handleStartBot}
>
{lang('BotStart')}
</Button>
</div>
)}
{IS_SINGLE_COLUMN_LAYOUT && renderingCanRestartBot && (
<div className="middle-column-footer-button-container" dir={lang.isRtl ? 'rtl' : undefined}>
<Button
size="tiny"
fluid
ripple
className="join-subscribe-button"
onClick={handleRestartBot}
>
{lang('BotRestart')}
</Button>
</div>
)}
<MessageSelectToolbar
messageListType={renderingMessageListType}
isActive={isSelectModeActive}
canPost={renderingCanPost}
/>
<PaymentModal
isOpen={Boolean(isPaymentModalOpen)}
onClose={closePaymentModal}
/>
<ReceiptModal
isOpen={Boolean(isReceiptModalOpen)}
onClose={clearReceipt}
/>
<SeenByModal isOpen={isSeenByModalOpen} />
<ReactorListModal isOpen={isReactorListModalOpen} />
</div>
</Transition> </Transition>
<ScrollDownButton <ScrollDownButton

View File

@ -371,7 +371,7 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
name={shouldSkipHistoryAnimations ? 'none' : 'slide-fade'} name={shouldSkipHistoryAnimations ? 'none' : 'slide-fade'}
activeKey={currentTransitionKey} activeKey={currentTransitionKey}
> >
{renderInfo} {renderInfo()}
</Transition> </Transition>
<GroupCallTopPane <GroupCallTopPane

View File

@ -322,11 +322,9 @@ const Invoice: FC<OwnProps & StateProps & GlobalStateProps> = ({
</div> </div>
{step !== undefined ? ( {step !== undefined ? (
<Transition name="slide" activeKey={step}> <Transition name="slide" activeKey={step}>
{() => ( <div className="content custom-scroll">
<div className="content custom-scroll"> {renderModalContent(step)}
{renderModalContent(step)} </div>
</div>
)}
</Transition> </Transition>
) : ( ) : (
<div className="empty-content"> <div className="empty-content">

View File

@ -445,7 +445,7 @@ const Profile: FC<OwnProps & StateProps> = ({
onStart={applyTransitionFix} onStart={applyTransitionFix}
onStop={handleTransitionStop} onStop={handleTransitionStop}
> >
{renderContent} {renderContent()}
</Transition> </Transition>
<TabList big activeTab={activeTab} tabs={tabs} onSwitchTab={setActiveTab} /> <TabList big activeTab={activeTab} tabs={tabs} onSwitchTab={setActiveTab} />
</div> </div>

View File

@ -428,7 +428,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
name={(shouldSkipTransition || shouldSkipAnimation) ? 'none' : 'slide-fade'} name={(shouldSkipTransition || shouldSkipAnimation) ? 'none' : 'slide-fade'}
activeKey={renderingContentKey} activeKey={renderingContentKey}
> >
{renderHeaderContent} {renderHeaderContent()}
</Transition> </Transition>
</div> </div>
); );

View File

@ -134,9 +134,7 @@ const SearchInput: FC<OwnProps> = ({
/> />
<i className="icon-search" /> <i className="icon-search" />
<ShowTransition isOpen={Boolean(isLoading)} className="slow"> <ShowTransition isOpen={Boolean(isLoading)} className="slow">
{() => ( <Loading color={spinnerColor} backgroundColor={spinnerBackgroundColor} onClick={onSpinnerClick} />
<Loading color={spinnerColor} backgroundColor={spinnerBackgroundColor} onClick={onSpinnerClick} />
)}
</ShowTransition> </ShowTransition>
{!isLoading && (value || canClose) && onReset && ( {!isLoading && (value || canClose) && onReset && (
<Button <Button

View File

@ -4,7 +4,6 @@ import useShowTransition from '../../hooks/useShowTransition';
import usePrevious from '../../hooks/usePrevious'; import usePrevious from '../../hooks/usePrevious';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
type ChildrenFn = () => any;
type OwnProps = { type OwnProps = {
isOpen: boolean; isOpen: boolean;
@ -12,7 +11,7 @@ type OwnProps = {
id?: string; id?: string;
className?: string; className?: string;
onClick?: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void; onClick?: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
children: ChildrenFn; children: React.ReactNode;
}; };
const ShowTransition: FC<OwnProps> = ({ const ShowTransition: FC<OwnProps> = ({
@ -23,7 +22,7 @@ const ShowTransition: FC<OwnProps> = ({
); );
const prevIsOpen = usePrevious(isOpen); const prevIsOpen = usePrevious(isOpen);
const prevChildren = usePrevious(children); const prevChildren = usePrevious(children);
const fromChildrenRef = useRef<ChildrenFn>(); const fromChildrenRef = useRef<React.ReactNode>();
if (prevIsOpen && !isOpen) { if (prevIsOpen && !isOpen) {
fromChildrenRef.current = prevChildren; fromChildrenRef.current = prevChildren;
@ -32,7 +31,7 @@ const ShowTransition: FC<OwnProps> = ({
return ( return (
shouldRender && ( shouldRender && (
<div id={id} className={buildClassName(className, transitionClassNames)} onClick={onClick}> <div id={id} className={buildClassName(className, transitionClassNames)} onClick={onClick}>
{isOpen ? children() : fromChildrenRef.current!()} {isOpen ? children : fromChildrenRef.current!}
</div> </div>
) )
); );

View File

@ -15,7 +15,7 @@ import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'
import './Transition.scss'; import './Transition.scss';
type ChildrenFn = (isActive: boolean, isFrom: boolean, currentKey: number) => any; export type ChildrenFn = (isActive: boolean, isFrom: boolean, currentKey: number) => React.ReactNode;
export type TransitionProps = { export type TransitionProps = {
ref?: RefObject<HTMLDivElement>; ref?: RefObject<HTMLDivElement>;
activeKey: number; activeKey: number;
@ -33,7 +33,7 @@ export type TransitionProps = {
className?: string; className?: string;
onStart?: NoneToVoidFunction; onStart?: NoneToVoidFunction;
onStop?: NoneToVoidFunction; onStop?: NoneToVoidFunction;
children: ChildrenFn; children: React.ReactNode | ChildrenFn;
}; };
const classNames = { const classNames = {
@ -65,7 +65,7 @@ const Transition: FC<TransitionProps> = ({
containerRef = ref; containerRef = ref;
} }
const rendersRef = useRef<Record<number, ChildrenFn>>({}); const rendersRef = useRef<Record<number, React.ReactNode | ChildrenFn>>({});
const prevActiveKey = usePrevious<any>(activeKey); const prevActiveKey = usePrevious<any>(activeKey);
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
@ -257,7 +257,11 @@ const Transition: FC<TransitionProps> = ({
} }
return ( return (
<div key={key} teactOrderKey={key}>{render(key === activeKey, key === prevActiveKey, activeKey)}</div> <div key={key} teactOrderKey={key}>{
typeof render === 'function'
? render(key === activeKey, key === prevActiveKey, activeKey)
: render
}</div>
); );
}); });