Skip to content

Commit

Permalink
Initial attempt at resizable container callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
davismcphee committed Sep 14, 2022
1 parent 634d341 commit 19aee7d
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React, { useState } from 'react';
import {
EuiText,
EuiResizableContainer,
EuiFlexGroup,
EuiFlexItem,
EuiStat,
EuiPanel,
} from '../../../../src/components';
import { fake } from 'faker';
import { useGeneratedHtmlId } from '../../../../src/services';

const text = (
<>
<p>{fake('{{lorem.paragraphs}}')}</p>
<p>{fake('{{lorem.paragraphs}}')}</p>
<p>{fake('{{lorem.paragraphs}}')}</p>
</>
);

export default () => {
const firstPanelId = useGeneratedHtmlId({ prefix: 'firstPanel' });
const secondPanelId = useGeneratedHtmlId({ prefix: 'secondPanel' });
const [resizeTrigger, setResizeTrigger] = useState();
const [sizes, setSizes] = useState({
[firstPanelId]: 50,
[secondPanelId]: 50,
});

return (
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel>
<EuiStat
title={resizeTrigger ?? ''}
titleSize="m"
description="Trigger"
isLoading={!resizeTrigger}
/>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel>
<EuiStat
title={`${Math.round(sizes[firstPanelId])}%`}
titleSize="m"
description="First panel"
isLoading={!resizeTrigger}
/>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel>
<EuiStat
title={`${Math.round(sizes[secondPanelId])}%`}
titleSize="m"
description="Second panel"
isLoading={!resizeTrigger}
/>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiResizableContainer
style={{ height: '200px' }}
onPanelWidthChange={(newSizes) => {
setSizes((prevSizes) => ({ ...prevSizes, ...newSizes }));
}}
onResizeStart={(trigger) => setResizeTrigger(trigger)}
onResizeEnd={() => setResizeTrigger(undefined)}
>
{(EuiResizablePanel, EuiResizableButton) => (
<>
<EuiResizablePanel
id={firstPanelId}
size={sizes[firstPanelId]}
minSize="30%"
>
<EuiText>
<div>{text}</div>
<a href="">Hello world</a>
</EuiText>
</EuiResizablePanel>

<EuiResizableButton />

<EuiResizablePanel
id={secondPanelId}
size={sizes[secondPanelId]}
minSize="200px"
>
<EuiText>{text}</EuiText>
</EuiResizablePanel>
</>
)}
</EuiResizableContainer>
</EuiFlexItem>
</EuiFlexGroup>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { PanelModeType } from '!!prop-loader!../../../../src/components/resizabl
import ResizableContainerBasic from './resizable_container_basic';
import ResizableContainerVertical from './resizable_container_vertical';
import ResizableContainerResetValues from './resizable_container_reset_values';
import ResizableContainerCallbacks from './resizable_container_callbacks';
import ResizablePanels from './resizable_panels';
import ResizablePanelCollapsible from './resizable_panel_collapsible';
import ResizablePanelCollapsibleResponsive from './resizable_panel_collapsible_responsive';
Expand All @@ -32,6 +33,7 @@ import ResizablePanelCollapsibleExt from './resizable_panel_collapsible_external
const ResizableContainerSource = require('!!raw-loader!./resizable_container_basic');
const ResizableContainerVerticalSource = require('!!raw-loader!./resizable_container_vertical');
const ResizableContainerResetValuesSource = require('!!raw-loader!./resizable_container_reset_values');
const ResizableContainerCallbacksSource = require('!!raw-loader!./resizable_container_callbacks');
const ResizablePanelsSource = require('!!raw-loader!./resizable_panels');
const ResizablePanelCollapsibleSource = require('!!raw-loader!./resizable_panel_collapsible');
const ResizablePanelCollapsibleResponsiveSource = require('!!raw-loader!./resizable_panel_collapsible_responsive');
Expand Down Expand Up @@ -313,6 +315,27 @@ export const ResizableContainerExample = {
demo: <ResizableContainerVertical />,
snippet: verticalSnippet,
},
{
source: [
{
type: GuideSectionTypes.JS,
code: ResizableContainerCallbacksSource,
},
],
title: 'Resizable container callbacks',
text: (
<>
<p>
<strong>EuiResizableContainer</strong> also provides action hooks
for parent components to access internal methods, such as{' '}
<strong>EuiResizablePanel</strong> collapse toggling. The actions
are accessible via the third parameter of the render prop function.
</p>
</>
),
demo: <ResizableContainerCallbacks />,
snippet: collapsibleExtSnippet,
},
{
source: [
{
Expand Down
5 changes: 3 additions & 2 deletions src/components/resizable_container/resizable_button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import { useEuiResizableContainerContext } from './context';
import {
EuiResizableButtonController,
EuiResizableButtonMouseEvent,
EuiResizableButtonKeyDownEvent,
EuiResizableButtonKeyEvent,
} from './types';

interface EuiResizableButtonControls {
onKeyDown: (eve: EuiResizableButtonKeyDownEvent) => void;
onKeyDown: (eve: EuiResizableButtonKeyEvent) => void;
onKeyUp: (eve: EuiResizableButtonKeyEvent) => void;
onMouseDown: (eve: EuiResizableButtonMouseEvent) => void;
onTouchStart: (eve: EuiResizableButtonMouseEvent) => void;
onFocus: (id: string) => void;
Expand Down
91 changes: 68 additions & 23 deletions src/components/resizable_container/resizable_container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
import { useContainerCallbacks, getPosition } from './helpers';
import {
EuiResizableButtonMouseEvent,
EuiResizableButtonKeyDownEvent,
EuiResizableButtonKeyEvent,
EuiResizableContainerState,
EuiResizableContainerActions,
} from './types';
Expand All @@ -46,6 +46,8 @@ const containerDirections = {
horizontal: 'horizontal',
};

type ResizeTrigger = 'pointer' | 'key';

export interface EuiResizableContainerProps
extends HTMLAttributes<HTMLDivElement>,
CommonProps {
Expand All @@ -68,6 +70,14 @@ export interface EuiResizableContainerProps
*/
onPanelWidthChange?: ({}: { [key: string]: number }) => any;
onToggleCollapsed?: ToggleCollapseCallback;
/**
* Called when resizing starts
*/
onResizeStart?: (trigger: ResizeTrigger) => any;
/**
* Called when resizing ends
*/
onResizeEnd?: (trigger: ResizeTrigger) => any;
style?: CSSProperties;
}

Expand All @@ -87,6 +97,8 @@ export const EuiResizableContainer: FunctionComponent<EuiResizableContainerProps
className,
onPanelWidthChange,
onToggleCollapsed,
onResizeStart,
onResizeEnd,
...rest
}) => {
const containerRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -122,6 +134,22 @@ export const EuiResizableContainer: FunctionComponent<EuiResizableContainerProps
}
}, [initialize, containerSize]);

const resizeTrigger = useRef<ResizeTrigger>();
const resizeStart = useCallback(
(trigger: ResizeTrigger) => {
onResizeStart?.(trigger);
resizeTrigger.current = trigger;
},
[onResizeStart]
);
const resizeEnd = useCallback(
(trigger: ResizeTrigger) => {
onResizeEnd?.(trigger);
resizeTrigger.current = undefined;
},
[onResizeEnd]
);

const onMouseDown = useCallback(
(event: EuiResizableButtonMouseEvent) => {
const currentTarget = event.currentTarget;
Expand All @@ -131,9 +159,10 @@ export const EuiResizableContainer: FunctionComponent<EuiResizableContainerProps
const prevPanelId = prevPanel!.id;
const nextPanelId = nextPanel!.id;
const position = getPosition(event, isHorizontal);
resizeStart('pointer');
actions.dragStart({ position, prevPanelId, nextPanelId });
},
[actions, isHorizontal]
[actions, isHorizontal, resizeStart]
);

const onMouseMove = useCallback(
Expand All @@ -160,45 +189,61 @@ export const EuiResizableContainer: FunctionComponent<EuiResizableContainerProps
]
);

const getDirection = useCallback(
(key: string) => {
let direction: 'forward' | 'backward' | null = null;
if (
(isHorizontal && key === keys.ARROW_LEFT) ||
(!isHorizontal && key === keys.ARROW_UP)
) {
direction = 'backward';
} else if (
(isHorizontal && key === keys.ARROW_RIGHT) ||
(!isHorizontal && key === keys.ARROW_DOWN)
) {
direction = 'forward';
}
return direction;
},
[isHorizontal]
);

const onKeyDown = useCallback(
(event: EuiResizableButtonKeyDownEvent) => {
(event: EuiResizableButtonKeyEvent) => {
const { key, currentTarget } = event;
const shouldResizeHorizontalPanel =
isHorizontal && (key === keys.ARROW_LEFT || key === keys.ARROW_RIGHT);
const shouldResizeVerticalPanel =
!isHorizontal && (key === keys.ARROW_UP || key === keys.ARROW_DOWN);
const direction = getDirection(key);
const prevPanelId = currentTarget.previousElementSibling!.id;
const nextPanelId = currentTarget.nextElementSibling!.id;
let direction;
if (key === keys.ARROW_DOWN || key === keys.ARROW_RIGHT) {
direction = 'forward';
}
if (key === keys.ARROW_UP || key === keys.ARROW_LEFT) {
direction = 'backward';
}

if (
direction === 'forward' ||
(direction === 'backward' &&
(shouldResizeHorizontalPanel || shouldResizeVerticalPanel) &&
prevPanelId &&
nextPanelId)
) {
if (direction && prevPanelId && nextPanelId) {
if (!event.repeat) {
resizeStart('key');
}
event.preventDefault();
actions.keyMove({ direction, prevPanelId, nextPanelId });
}
},
[actions, isHorizontal]
[actions, getDirection, resizeStart]
);

const onKeyUp = useCallback(() => {
if (resizeTrigger.current === 'key') {
resizeEnd('key');
}
}, [resizeEnd]);

const onMouseUp = useCallback(() => {
if (resizeTrigger.current === 'pointer') {
resizeEnd('pointer');
}
actions.reset();
}, [actions]);
}, [actions, resizeEnd]);

// eslint-disable-next-line react-hooks/exhaustive-deps
const EuiResizableButton = useCallback(
euiResizableButtonWithControls({
onKeyDown,
onKeyUp,
onMouseDown,
onTouchStart: onMouseDown,
onFocus: actions.resizerFocus,
Expand Down
2 changes: 1 addition & 1 deletion src/components/resizable_container/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type EuiResizableButtonMouseEvent =
| MouseEvent<HTMLButtonElement>
| TouchEvent<HTMLButtonElement>;

export type EuiResizableButtonKeyDownEvent = KeyboardEvent<HTMLButtonElement>;
export type EuiResizableButtonKeyEvent = KeyboardEvent<HTMLButtonElement>;

export interface EuiResizableContainerState {
isDragging: boolean;
Expand Down

0 comments on commit 19aee7d

Please sign in to comment.