Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions @types/react-transition-group/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable */
export * from '../../node_modules/@types/react-transition-group';

// Require nodeRef in CSSTransition to support React 19 (no findDOMNode)
import { Component } from 'react';
import { CSSTransitionProps } from '../../node_modules/@types/react-transition-group/CSSTransition';
declare class CSSTransition<Ref extends HTMLElement> extends Component<
CSSTransitionProps<Ref> & { nodeRef: React.Ref<Ref> }
> {}
export { CSSTransition };
71 changes: 45 additions & 26 deletions packages/components/src/Collapse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,6 @@ class Collapse extends Component<CollapseProps> {
'data-testid': undefined,
};

static handleEnter(elemParam: HTMLElement): void {
const elem = elemParam;
elem.style.height = '0';
}

static handleEntering(elemParam: HTMLElement): void {
const elem = elemParam;
elem.style.height = `${Collapse.getHeight(elem)}px`;
}

static handleExiting(elemParam: HTMLElement): void {
const elem = elemParam;
elem.style.height = '0';
}

static handleExit(elemParam: HTMLElement): void {
const elem = elemParam;
elem.style.height = `${Collapse.getHeight(elem)}px`;
}

static getHeight(elem: HTMLElement): number {
const scrollBarWidth = elem.scrollWidth - elem.clientWidth;
return elem.scrollHeight - scrollBarWidth;
Expand All @@ -49,8 +29,13 @@ class Collapse extends Component<CollapseProps> {
this.handleEntered = this.handleEntered.bind(this);
}

handleEntered(elemParam: HTMLElement): void {
const elem = elemParam;
nodeRef = React.createRef<HTMLDivElement>();

handleEntered(): void {
const elem = this.nodeRef.current;
if (!elem) {
return;
}
elem.style.height = '';

const { autoFocusOnShow } = this.props;
Expand All @@ -65,6 +50,38 @@ class Collapse extends Component<CollapseProps> {
}
}

handleEnter(): void {
const elem = this.nodeRef.current;
if (!elem) {
return;
}
elem.style.height = '0';
}

handleEntering(): void {
const elem = this.nodeRef.current;
if (!elem) {
return;
}
elem.style.height = `${Collapse.getHeight(elem)}px`;
}

handleExiting(): void {
const elem = this.nodeRef.current;
if (!elem) {
return;
}
elem.style.height = '0';
}

handleExit(): void {
const elem = this.nodeRef.current;
if (!elem) {
return;
}
elem.style.height = `${Collapse.getHeight(elem)}px`;
}

render(): JSX.Element {
const {
children,
Expand All @@ -81,15 +98,17 @@ class Collapse extends Component<CollapseProps> {
exitActive: 'collapsing',
exitDone: 'collapse',
}}
onEnter={Collapse.handleEnter}
onEntering={Collapse.handleEntering}
onEnter={this.handleEnter}
onEntering={this.handleEntering}
onEntered={this.handleEntered}
onExit={Collapse.handleExit}
onExiting={Collapse.handleExiting}
onExit={this.handleExit}
onExiting={this.handleExiting}
timeout={350}
nodeRef={this.nodeRef}
>
{state => (
<div
ref={this.nodeRef}
className={classNames({ collapse: state === 'exited' }, className)}
data-testid={dataTestId}
>
Expand Down
10 changes: 8 additions & 2 deletions packages/components/src/LoadingOverlay.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef } from 'react';
import { CSSTransition } from 'react-transition-group';
import classNames from 'classnames';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
Expand Down Expand Up @@ -29,6 +29,7 @@ function LoadingOverlay({
timeout = ThemeExport.transitionMs,
'data-testid': dataTestId,
}: LoadingOverlayProps): JSX.Element {
const nodeRef = useRef<HTMLDivElement>(null);
const messageTestId =
dataTestId != null ? `${dataTestId}-message` : undefined;
const spinnerTestId =
Expand All @@ -41,8 +42,13 @@ function LoadingOverlay({
classNames={classNames(className, { fade: isLoaded })}
mountOnEnter
unmountOnExit
nodeRef={nodeRef}
>
<div className="fill-parent-absolute" data-testid={dataTestId}>
<div
ref={nodeRef}
className="fill-parent-absolute"
data-testid={dataTestId}
>
<div
className={classNames(
'iris-panel-message-overlay',
Expand Down
5 changes: 4 additions & 1 deletion packages/components/src/ToastNotification.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef } from 'react';
import classNames from 'classnames';
import { CSSTransition } from 'react-transition-group';
import { vsClose } from '@deephaven/icons';
Expand Down Expand Up @@ -31,6 +31,7 @@ function ToastNotification({
onDismiss,
'data-testid': dataTestId,
}: ToastNotificationProps): JSX.Element {
const nodeRef = useRef<HTMLDivElement>(null);
const hasButtons = buttons && buttons.length !== 0;

return (
Expand All @@ -40,8 +41,10 @@ function ToastNotification({
classNames="toast-notification-slide-up"
mountOnEnter
unmountOnExit
nodeRef={nodeRef}
>
<div
ref={nodeRef}
className={classNames('toast-notification', classNamesProp, type)}
role="presentation"
onClick={onClick}
Expand Down
4 changes: 4 additions & 0 deletions packages/components/src/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function Modal({
}: ModalProps): React.ReactElement | null {
const element = useRef<HTMLElement>();
const background = useRef<HTMLDivElement>(null);
const backdropFade = useRef<HTMLDivElement>(null);
const [backgroundClicked, setBackgroundClicked] = useState(false);

const handleKeyDown = useCallback(
Expand Down Expand Up @@ -118,8 +119,10 @@ function Modal({
}}
timeout={ThemeExport.transitionMs}
onExited={onExited}
nodeRef={backdropFade}
>
<div
ref={backdropFade}
className={classNames('modal-backdrop fade')}
style={{ zIndex: 1050 }}
/>
Expand All @@ -134,6 +137,7 @@ function Modal({
}}
timeout={ThemeExport.transitionMs}
onExited={onExited}
nodeRef={background}
>
<div
ref={background}
Expand Down
4 changes: 4 additions & 0 deletions packages/components/src/popper/Popper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ class Popper extends Component<PopperProps, PopperState> {

container: React.RefObject<HTMLDivElement>;

nodeRef = React.createRef<HTMLDivElement>();

// This is the request animation frame handle number
rAF: number;

Expand Down Expand Up @@ -256,8 +258,10 @@ class Popper extends Component<PopperProps, PopperState> {
classNames="popper-transition"
onEntered={this.handleEnter}
onExited={this.handleExit}
nodeRef={this.nodeRef}
>
<div
ref={this.nodeRef}
onClick={e => {
// stop click events from escaping popper
e.stopPropagation();
Expand Down
56 changes: 47 additions & 9 deletions packages/components/src/transitions/FadeTransition.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,70 @@
import { useCallback, useRef } from 'react';
import { CSSTransition } from 'react-transition-group';
import type { CSSTransitionProps } from 'react-transition-group/CSSTransition';
import type { EndHandler } from 'react-transition-group/Transition';
import type {
EndHandler,
EnterHandler,
} from 'react-transition-group/Transition';
import classNames from 'classnames';
import type { RemoveIndexSignature } from '@deephaven/utils';
import ThemeExport from '../ThemeExport';

type FadeTransitionProps<Ref extends undefined | HTMLElement = undefined> =
const DISPLAY_CONTENTS = { display: 'contents' };

type FadeTransitionProps =
// We default the timeout, so user doesn't need to provide it
// However, CSSTransitionProps get confused if you don't provide a timeout, it requires an endHandler
// We're just making the endHandler optional here, as the timeout has a default
Omit<CSSTransitionProps<Ref>, 'addEndHandler'> & {
addEndHandler?: EndHandler<Ref> | undefined;
Omit<
RemoveIndexSignature<CSSTransitionProps<HTMLElement>>,
'addEndHandler' | 'children' | 'onEnter'
> & {
addEndHandler?: EndHandler<HTMLElement> | undefined;
onEnter?: EnterHandler<undefined>;
children?: React.ReactNode;
};

/**
* Fade between two components. Defaults to 150ms transition.
*/
function FadeTransition<Ref extends undefined | HTMLElement = undefined>({
className,
function FadeTransition({
classNames: classNamesProp,
timeout = ThemeExport.transitionMs,
children,
onEnter,
...props
}: FadeTransitionProps<Ref>): JSX.Element {
}: FadeTransitionProps): JSX.Element {
const nodeRef = useRef<HTMLElement | null>(null);
const handleOnEnter = useCallback(
(isAppearing: boolean) => {
const elem = nodeRef.current;
if (!elem) {
return;
}
onEnter?.(elem, isAppearing);
},
[onEnter]
);

// Mimics findDOMNode for CSSTransition
// The ref should be set before CSSTransition does anything with it
const setRef = useCallback((node: HTMLElement | null) => {
nodeRef.current = node?.firstElementChild as HTMLElement;
}, []);

return (
<CSSTransition
nodeRef={nodeRef}
timeout={timeout}
classNames={classNames('fade', className)}
classNames={classNames('fade', classNamesProp)}
onEnter={handleOnEnter}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
>
<div ref={setRef} style={DISPLAY_CONTENTS}>
{children}
</div>
</CSSTransition>
);
}

Expand Down
37 changes: 28 additions & 9 deletions packages/components/src/transitions/SlideTransition.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
// SlideTransition class uses CSSTransition with slide-left and slide-right classNames, depending on the prop direction. The transition is 250ms long.
//
import { useCallback, useRef } from 'react';
import { CSSTransition } from 'react-transition-group';
import type { CSSTransitionProps } from 'react-transition-group/CSSTransition';
import type { EndHandler } from 'react-transition-group/Transition';

import classNames from 'classnames';
import type { RemoveIndexSignature } from '@deephaven/utils';
import ThemeExport from '../ThemeExport';

const DISPLAY_CONTENTS = { display: 'contents' };

type SlideDirection = 'left' | 'right';

type SlideTransitionProps<Ref extends undefined | HTMLElement = undefined> =
type SlideTransitionProps =
// We default the timeout, so user doesn't need to provide it
// However, CSSTransitionProps get confused if you don't provide a timeout, it requires an endHandler
// We're just making the endHandler optional here, as the timeout has a default
Omit<CSSTransitionProps<Ref>, 'addEndHandler'> & {
addEndHandler?: EndHandler<Ref> | undefined;
} & {
Omit<
RemoveIndexSignature<CSSTransitionProps<HTMLElement>>,
'addEndHandler'
> & {
addEndHandler?: EndHandler<HTMLElement> | undefined;
children?: React.ReactNode;
/**
* The direction of the slide transition. Defaults to left.
*/
Expand All @@ -31,19 +36,33 @@ type SlideTransitionProps<Ref extends undefined | HTMLElement = undefined> =
*/
function SlideTransition({
direction = 'left',
className,
classNames: classNamesProp,
children,

/** Defaults to mid */
timeout = ThemeExport.transitionMidMs,
...props
}: SlideTransitionProps): JSX.Element {
const nodeRef = useRef<HTMLElement | null>(null);

// Mimics findDOMNode for CSSTransition
// The ref should be set before CSSTransition does anything with it
const setRef = useCallback((node: HTMLElement | null) => {
nodeRef.current = node?.firstElementChild as HTMLElement;
}, []);

return (
<CSSTransition
nodeRef={nodeRef}
timeout={timeout}
classNames={classNames(`slide-${direction}`, className)}
classNames={classNames(`slide-${direction}`, classNamesProp)}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
>
<div ref={setRef} style={DISPLAY_CONTENTS}>
{children}
</div>
</CSSTransition>
);
}

Expand Down
Loading
Loading