From de1fefa78dd2c98051a24084fa3cc365b661a460 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Mon, 6 Jul 2020 13:32:09 -0600 Subject: [PATCH 1/2] Bugfixes for resizable panels including arrow keys, nested panels, and mixed button sizes --- .../resizable_container/context.tsx | 13 ++++++++++ src/components/resizable_container/helpers.ts | 24 +++++++++---------- .../resizable_container/resizable_button.tsx | 22 +++++++++++++++++ .../resizable_container.tsx | 3 ++- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/components/resizable_container/context.tsx b/src/components/resizable_container/context.tsx index 708db17a0ea..1be0fbd052d 100644 --- a/src/components/resizable_container/context.tsx +++ b/src/components/resizable_container/context.tsx @@ -28,6 +28,7 @@ export interface EuiResizablePanelController { export class EuiResizablePanelRegistry { private panels: { [key: string]: EuiResizablePanelController } = {}; + private resizerRefs = new Set(); registerPanel(panel: EuiResizablePanelController) { this.panels[panel.id] = panel; @@ -37,10 +38,22 @@ export class EuiResizablePanelRegistry { delete this.panels[id]; } + registerResizerRef(resizerRef: HTMLElement) { + this.resizerRefs.add(resizerRef); + } + + deregisterResizerRef(resizerRef: HTMLElement) { + this.resizerRefs.delete(resizerRef); + } + getResizerSiblings(prevPanelId: string, nextPanelId: string) { return [this.panels[prevPanelId], this.panels[nextPanelId]]; } + getAllResizers() { + return Array.from(this.resizerRefs); + } + fetchAllPanels( prevPanelId: string, nextPanelId: string, diff --git a/src/components/resizable_container/helpers.ts b/src/components/resizable_container/helpers.ts index 5e271c33f21..27caa43407d 100644 --- a/src/components/resizable_container/helpers.ts +++ b/src/components/resizable_container/helpers.ts @@ -77,15 +77,13 @@ export const useContainerCallbacks = ({ const getResizerButtonsSize = useCallback(() => { // get sum of all of resizer button sizes to proper calculate panels ratio - const allResizers = containerRef.current!.getElementsByClassName( - 'euiResizableButton' - ) as HTMLCollectionOf; - const size = isHorizontal - ? allResizers[0].offsetWidth - : allResizers[0].offsetHeight; - - return size * allResizers.length; - }, [containerRef, isHorizontal]); + const allResizers = registryRef.current.getAllResizers(); + return allResizers.reduce( + (size, resizer) => + size + (isHorizontal ? resizer.offsetWidth : resizer.offsetHeight), + 0 + ); + }, [registryRef, isHorizontal]); const onMouseDown = useCallback( (event: EuiResizableButtonMouseEvent) => { @@ -150,16 +148,16 @@ export const useContainerCallbacks = ({ nextPanelId, containerSize - resizersSize ); - if (prevPanelSize !== nextPanelSize && onPanelWidthChange) { + + if (onPanelWidthChange) { onPanelWidthChange({ ...panelObject, [prevPanelId]: prevPanelSize, [nextPanelId]: nextPanelSize, }); - - prevPanel.setSize(prevPanelSize); - nextPanel.setSize(nextPanelSize); } + prevPanel.setSize(prevPanelSize); + nextPanel.setSize(nextPanelSize); } }, // `setState` is safe to omit from `useCallback` diff --git a/src/components/resizable_container/resizable_button.tsx b/src/components/resizable_container/resizable_button.tsx index 8806a0a2324..9d29ad319ab 100644 --- a/src/components/resizable_container/resizable_button.tsx +++ b/src/components/resizable_container/resizable_button.tsx @@ -23,11 +23,14 @@ import React, { KeyboardEvent, MouseEvent, TouchEvent, + useCallback, + useRef, } from 'react'; import classNames from 'classnames'; import { CommonProps } from '../common'; import { EuiI18n } from '../i18n'; +import { EuiResizablePanelRegistry } from './context'; export type EuiResizableButtonMouseEvent = | MouseEvent @@ -41,6 +44,7 @@ interface EuiResizableButtonControls { onMouseDown: (eve: EuiResizableButtonMouseEvent) => void; onTouchStart: (eve: EuiResizableButtonMouseEvent) => void; isHorizontal: boolean; + registryRef: React.MutableRefObject; } export interface EuiResizableButtonProps @@ -69,6 +73,7 @@ export const EuiResizableButton: FunctionComponent = ({ isHorizontal, className, size = 'm', + registryRef, ...rest }) => { const classes = classNames( @@ -81,6 +86,22 @@ export const EuiResizableButton: FunctionComponent = ({ className ); + const previousRef = useRef(); + const onRef = useCallback( + (ref: HTMLElement | null) => { + if (ref) { + previousRef.current = ref; + registryRef!.current.registerResizerRef(ref); + } else { + if (previousRef.current != null) { + registryRef!.current.deregisterResizerRef(previousRef.current); + previousRef.current = undefined; + } + } + }, + [registryRef] + ); + const setFocus = (e: MouseEvent) => e.currentTarget.focus(); @@ -96,6 +117,7 @@ export const EuiResizableButton: FunctionComponent = ({ ]}> {([horizontalResizerAriaLabel, verticalResizerAriaLabel]: string[]) => (