Skip to content

Commit

Permalink
[EuiResizableContainer] Allow dynamic direction (#4557)
Browse files Browse the repository at this point in the history
* dynamic isHorizontal; prevent reregistry errors

* revert me

* required param

* remove useMemo

* docs; CL

* Revert "revert me"

This reverts commit 55f82af.

* docs section

* useIsWithinBreakpoints export
  • Loading branch information
thompsongl authored Feb 24, 2021
1 parent 95efcbb commit 48ddc80
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 17 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [`master`](https://github.com/elastic/eui/tree/master)

No public interface changes since `31.8.0`.
- Allowed dynamically changing the `direction` prop on `EuiResizableContainer` ([#4557](https://github.com/elastic/eui/pull/4557))
- Exported `useIsWithinBreakpoints` hook ([#4557](https://github.com/elastic/eui/pull/4557))

## [`31.8.0`](https://github.com/elastic/eui/tree/v31.8.0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import ResizableContainerVertical from './resizable_container_vertical';
import ResizableContainerResetValues from './resizable_container_reset_values';
import ResizablePanels from './resizable_panels';
import ResizablePanelCollapsible from './resizable_panel_collapsible';
import ResizablePanelCollapsibleResponsive from './resizable_panel_collapsible_responsive';
import ResizablePanelCollapsibleOpts from './resizable_panel_collapsible_options';
import ResizablePanelCollapsibleExt from './resizable_panel_collapsible_external';

Expand All @@ -35,6 +36,7 @@ const ResizableContainerVerticalSource = require('!!raw-loader!./resizable_conta
const ResizableContainerResetValuesSource = require('!!raw-loader!./resizable_container_reset_values');
const ResizablePanelsSource = require('!!raw-loader!./resizable_panels');
const ResizablePanelCollapsibleSource = require('!!raw-loader!./resizable_panel_collapsible');
const ResizablePanelCollapsibleResponsiveSource = require('!!raw-loader!./resizable_panel_collapsible_responsive');
const ResizablePanelCollapsibleOptsSource = require('!!raw-loader!./resizable_panel_collapsible_options');
const ResizablePanelCollapsibleExtSource = require('!!raw-loader!./resizable_panel_collapsible_external');

Expand Down Expand Up @@ -105,6 +107,28 @@ const collapsibleSnippet = `<EuiResizableContainer>
</>
)}
</EuiResizableContainer>`;
const ResizablePanelCollapsibleResponsiveHtml = renderToHtml(
ResizablePanelCollapsibleResponsive
);
const responsiveSnippet = `<EuiResizableContainer direction={isMobile ? 'vertical' : 'horizontal'}>
{(EuiResizablePanel, EuiResizableButton) => (
<>
<EuiResizablePanel mode="collapsible" initialSize={20} minSize="5px">
<EuiText>
<p>{text}</p>
</EuiText>
</EuiResizablePanel>
<EuiResizableButton />
<EuiResizablePanel mode="main" initialSize={80} minSize="200px">
<EuiText>
<p>{text}</p>
</EuiText>
</EuiResizablePanel>
</>
)}
</EuiResizableContainer>`;
const ResizablePanelCollapsibleOptsHtml = renderToHtml(
ResizablePanelCollapsibleOpts
);
Expand Down Expand Up @@ -366,6 +390,30 @@ export const ResizableContainerExample = {
demo: <ResizablePanelCollapsible />,
snippet: collapsibleSnippet,
},
{
source: [
{
type: GuideSectionTypes.JS,
code: ResizablePanelCollapsibleResponsiveSource,
},
{
type: GuideSectionTypes.HTML,
code: ResizablePanelCollapsibleResponsiveHtml,
},
],
title: 'Responsive layout',
text: (
<div>
<p>
It is possible to dynamically change the{' '}
<EuiCode>direction</EuiCode> prop to allow for adapting layouts to
screen size. Resize the window to see the panel orientation change.
</p>
</div>
),
snippet: responsiveSnippet,
demo: <ResizablePanelCollapsibleResponsive />,
},
{
source: [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { useState } from 'react';
import {
EuiText,
EuiResizableContainer,
EuiListGroup,
EuiListGroupItem,
EuiPanel,
EuiTitle,
EuiSpacer,
EuiPage,
} from '../../../../src/components';
import { useIsWithinBreakpoints } from '../../../../src/services';
import { fake } from 'faker';

const texts = [];

for (let i = 0; i < 4; i++) {
texts.push(<p>{fake('{{lorem.paragraph}}')}</p>);
}

export default () => {
const items = [
{
id: 1,
label: 'First item',
text: texts[0],
active: true,
},
{
id: 2,
label: 'Second item',
text: texts[1],
},
{
id: 3,
label: 'Third item',
text: texts[2],
},
{
id: 4,
label: 'Forth item',
text: texts[3],
},
];

const [itemSelected, setItemSelected] = useState(items[0]);
const itemElements = items.map((item, index) => (
<EuiListGroupItem
key={index}
onClick={() => setItemSelected(item)}
label={item.label}
size="s"
/>
));

const isMobile = useIsWithinBreakpoints(['xs', 's']);
const style = isMobile ? { height: '100%' } : { minHeight: '100%' };

return (
<EuiPage paddingSize="none">
<EuiResizableContainer
direction={isMobile ? 'vertical' : 'horizontal'}
style={{ height: '400px' }}>
{(EuiResizablePanel, EuiResizableButton) => (
<>
<EuiResizablePanel
mode="collapsible"
initialSize={20}
minSize="10%">
<EuiListGroup flush>{itemElements}</EuiListGroup>
</EuiResizablePanel>

<EuiResizableButton />

<EuiResizablePanel mode="main" initialSize={80} minSize="50px">
<EuiPanel paddingSize="l" style={style}>
<EuiTitle>
<p>{itemSelected.label}</p>
</EuiTitle>
<EuiSpacer />
<EuiText>{itemSelected.text}</EuiText>
</EuiPanel>
</EuiResizablePanel>
</>
)}
</EuiResizableContainer>
</EuiPage>
);
};
11 changes: 7 additions & 4 deletions src/components/resizable_container/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ export const useContainerCallbacks = ({
state: EuiResizableContainerState,
action: EuiResizableContainerAction
): EuiResizableContainerState {
const getContainerSize = () => {
return state.isHorizontal
const getContainerSize = (isHorizontal: boolean) => {
return isHorizontal
? containerRef.current!.getBoundingClientRect().width
: containerRef.current!.getBoundingClientRect().height;
};
Expand All @@ -153,9 +153,11 @@ export const useContainerCallbacks = ({

switch (action.type) {
case 'EUI_RESIZABLE_CONTAINER_INIT': {
const { isHorizontal } = action.payload;
return {
...state,
containerSize: getContainerSize(),
isHorizontal,
containerSize: getContainerSize(isHorizontal),
};
}
case 'EUI_RESIZABLE_PANEL_REGISTER': {
Expand Down Expand Up @@ -547,9 +549,10 @@ export const useContainerCallbacks = ({
const actions: EuiResizableContainerActions = useMemo(() => {
return {
reset: () => dispatch({ type: 'EUI_RESIZABLE_RESET' }),
initContainer: () =>
initContainer: (isHorizontal: boolean) =>
dispatch({
type: 'EUI_RESIZABLE_CONTAINER_INIT',
payload: { isHorizontal },
}),
registerPanel: (panel: EuiResizablePanelController) =>
dispatch({
Expand Down
8 changes: 4 additions & 4 deletions src/components/resizable_container/resizable_container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,15 @@ export const EuiResizableContainer: FunctionComponent<EuiResizableContainerProps
onPanelWidthChange,
});

const initialize = useCallback(() => {
actions.initContainer();
}, [actions]);

const containerSize = useResizeObserver(
containerRef.current,
isHorizontal ? 'width' : 'height'
);

const initialize = useCallback(() => {
actions.initContainer(isHorizontal);
}, [actions, isHorizontal]);

useEffect(() => {
if (containerSize.width > 0 && containerSize.height > 0) {
initialize();
Expand Down
13 changes: 7 additions & 6 deletions src/components/resizable_container/resizable_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -359,32 +359,33 @@ export const EuiResizablePanel: FunctionComponent<EuiResizablePanelProps> = ({
(modeType === 'custom' && isCollapsed) || isCollapsible;

let theToggle;
let theResizer;
if ((isCollapsible || modeType === 'custom') && hasLeftToggle) {
theResizer = resizers[resizerIds.current[0]];
theToggle = (
<EuiResizableCollapseButton
externalPosition="before"
direction={isHorizontal ? 'horizontal' : 'vertical'}
isVisible={
resizers[resizerIds.current[0]].isFocused ||
resizers[resizerIds.current[0]].isDisabled
theResizer && (theResizer.isFocused || theResizer.isDisabled)
}
isCollapsed={resizers[resizerIds.current[0]].isDisabled}
isCollapsed={theResizer && theResizer.isDisabled}
internalPosition={toggleOpts.position as ToggleOptions['position']}
data-test-subj={toggleOpts['data-test-subj']}
aria-label={toggleButtonAriaLabel}
onClick={collapseRight}
/>
);
} else if ((isCollapsible || modeType === 'custom') && hasRightToggle) {
theResizer = resizers[resizerIds.current[1]];
theToggle = (
<EuiResizableCollapseButton
externalPosition="after"
direction={isHorizontal ? 'horizontal' : 'vertical'}
isVisible={
resizers[resizerIds.current[1]].isFocused ||
resizers[resizerIds.current[1]].isDisabled
theResizer && (theResizer.isFocused || theResizer.isDisabled)
}
isCollapsed={resizers[resizerIds.current[1]].isDisabled}
isCollapsed={theResizer && theResizer.isDisabled}
internalPosition={toggleOpts.position as ToggleOptions['position']}
data-test-subj={toggleOpts['data-test-subj']}
aria-label={toggleButtonAriaLabel}
Expand Down
3 changes: 2 additions & 1 deletion src/components/resizable_container/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ interface ActionReset {

interface ActionInit {
type: 'EUI_RESIZABLE_CONTAINER_INIT';
payload: { isHorizontal: boolean };
}

export interface ActionDragStart {
Expand Down Expand Up @@ -169,7 +170,7 @@ export type EuiResizableContainerAction =

export interface EuiResizableContainerActions {
reset: () => void;
initContainer: () => void;
initContainer: (isHorizontal: boolean) => void;
registerPanel: (panel: EuiResizablePanelController) => void;
deregisterPanel: (panelId: EuiResizablePanelController['id']) => void;
registerResizer: (resizer: EuiResizableButtonController) => void;
Expand Down
6 changes: 5 additions & 1 deletion src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,8 @@ export {

export { EuiWindowEvent } from './window_event';

export { useCombinedRefs, useDependentState } from './hooks';
export {
useCombinedRefs,
useDependentState,
useIsWithinBreakpoints,
} from './hooks';

0 comments on commit 48ddc80

Please sign in to comment.