Skip to content

Commit 225db60

Browse files
tkajtochacstll
authored andcommitted
[Flyout System] Support resizing flyouts (elastic#8999)
1 parent baaa271 commit 225db60

File tree

9 files changed

+358
-229
lines changed

9 files changed

+358
-229
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Added a new optional `resizable` (boolean) prop to `EuiFlyout`. Resizability can now be controlled dynamically without the need to use `EuiFlyoutResizable`.

packages/eui/src/components/collapsible_nav_beta/collapsible_nav_beta.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { EuiCollapsibleNavButton } from './collapsible_nav_button';
3535
import { euiCollapsibleNavBetaStyles } from './collapsible_nav_beta.styles';
3636

3737
export type EuiCollapsibleNavBetaProps = CommonProps &
38-
HTMLAttributes<HTMLElement> &
38+
Omit<HTMLAttributes<HTMLElement>, 'onResize'> &
3939
Pick<
4040
EuiFlyoutProps, // Extend only specific flyout props - EuiCollapsibleNav is much less customizable than EuiFlyout
4141
'side' | 'focusTrapProps' | 'includeFixedHeadersInFocusTrap'

packages/eui/src/components/flyout/__snapshots__/flyout.test.tsx.snap

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,52 +1051,58 @@ exports[`EuiFlyout props size accepts custom number 1`] = `
10511051
`;
10521052

10531053
exports[`EuiFlyout props size fill is rendered 1`] = `
1054-
[
1054+
<body
1055+
class="euiBody--hasFlyout"
1056+
style=""
1057+
>
10551058
<div>
1056-
<div
1057-
data-focus-guard="true"
1058-
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
1059-
tabindex="0"
1060-
/>
1061-
<div
1062-
data-focus-lock-disabled="false"
1063-
>
1059+
<div>
10641060
<div
1065-
aria-describedby="generated-id"
1066-
aria-modal="true"
1067-
class="euiFlyout emotion-euiFlyout-l-fill-noMaxWidth-overlay-right-right"
1068-
data-autofocus="true"
1069-
role="dialog"
1061+
data-focus-guard="true"
1062+
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
10701063
tabindex="0"
1064+
/>
1065+
<div
1066+
data-focus-lock-disabled="false"
10711067
>
1072-
<p
1073-
class="emotion-euiScreenReaderOnly"
1074-
id="generated-id"
1075-
>
1076-
You are in a modal dialog. Press Escape or tap/click outside the dialog on the shadowed overlay to close.
1077-
</p>
1078-
<button
1079-
aria-label="Close this dialog"
1080-
class="euiButtonIcon euiFlyout__closeButton emotion-euiButtonIcon-xs-empty-text-euiFlyout__closeButton-inside"
1081-
data-test-subj="euiFlyoutCloseButton"
1082-
type="button"
1068+
<div
1069+
aria-describedby="generated-id"
1070+
aria-modal="true"
1071+
class="euiFlyout emotion-euiFlyout-l-fill-noMaxWidth-overlay-right-right"
1072+
data-autofocus="true"
1073+
role="dialog"
1074+
tabindex="0"
10831075
>
1084-
<span
1085-
aria-hidden="true"
1086-
class="euiButtonIcon__icon"
1087-
color="inherit"
1088-
data-euiicon-type="cross"
1089-
/>
1090-
</button>
1076+
<p
1077+
class="emotion-euiScreenReaderOnly"
1078+
id="generated-id"
1079+
>
1080+
You are in a modal dialog. Press Escape or tap/click outside the dialog on the shadowed overlay to close.
1081+
1082+
</p>
1083+
<button
1084+
aria-label="Close this dialog"
1085+
class="euiButtonIcon euiFlyout__closeButton emotion-euiButtonIcon-xs-empty-text-euiFlyout__closeButton-inside"
1086+
data-test-subj="euiFlyoutCloseButton"
1087+
type="button"
1088+
>
1089+
<span
1090+
aria-hidden="true"
1091+
class="euiButtonIcon__icon"
1092+
color="inherit"
1093+
data-euiicon-type="cross"
1094+
/>
1095+
</button>
1096+
</div>
10911097
</div>
1098+
<div
1099+
data-focus-guard="true"
1100+
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
1101+
tabindex="0"
1102+
/>
10921103
</div>
1093-
<div
1094-
data-focus-guard="true"
1095-
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
1096-
tabindex="0"
1097-
/>
1098-
</div>,
1099-
]
1104+
</div>
1105+
</body>
11001106
`;
11011107

11021108
exports[`EuiFlyout props size l is rendered 1`] = `

packages/eui/src/components/flyout/flyout_resizable.styles.ts renamed to packages/eui/src/components/flyout/_flyout_resize_button.styles.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@
77
*/
88

99
import { css } from '@emotion/react';
10-
11-
import { UseEuiTheme } from '../../services';
1210
import { logicalCSS } from '../../global_styling';
11+
import { UseEuiTheme } from '../../services';
1312

14-
export const euiFlyoutResizableButtonStyles = ({ euiTheme }: UseEuiTheme) => ({
15-
euiFlyoutResizableButton: css`
13+
export const euiFlyoutResizeButtonStyles = ({ euiTheme }: UseEuiTheme) => ({
14+
root: css`
1615
position: absolute;
1716
`,
1817
overlay: {
@@ -32,7 +31,7 @@ export const euiFlyoutResizableButtonStyles = ({ euiTheme }: UseEuiTheme) => ({
3231
`,
3332
},
3433
noOverlay: {
35-
noOverlay: css`
34+
root: css`
3635
margin-inline: 0;
3736
`,
3837
left: css`
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import React from 'react';
10+
import { useEuiMemoizedStyles } from '../../services';
11+
import {
12+
EuiResizableButton,
13+
EuiResizableButtonProps,
14+
} from '../resizable_container';
15+
import type { _EuiFlyoutType, _EuiFlyoutSide } from './const';
16+
import type { EuiFlyoutComponentProps } from './flyout.component';
17+
import { euiFlyoutResizeButtonStyles } from './_flyout_resize_button.styles';
18+
19+
type EuiFlyoutResizeButtonProps = Pick<
20+
EuiResizableButtonProps,
21+
'onMouseDown' | 'onKeyDown' | 'onTouchStart'
22+
> & {
23+
type: _EuiFlyoutType;
24+
side: _EuiFlyoutSide;
25+
ownFocus: EuiFlyoutComponentProps['ownFocus'];
26+
isPushed: boolean;
27+
};
28+
29+
export const EuiFlyoutResizeButton = ({
30+
type,
31+
side,
32+
ownFocus,
33+
isPushed,
34+
...resizableButtonProps
35+
}: EuiFlyoutResizeButtonProps) => {
36+
const hasOverlay = ownFocus && type === 'overlay';
37+
const styles = useEuiMemoizedStyles(euiFlyoutResizeButtonStyles);
38+
39+
const cssStyles = [
40+
styles.root,
41+
styles[type][side],
42+
!hasOverlay && styles.noOverlay.root,
43+
!hasOverlay && styles.noOverlay[side],
44+
];
45+
46+
return (
47+
<EuiResizableButton
48+
isHorizontal
49+
indicator="border"
50+
css={cssStyles}
51+
{...resizableButtonProps}
52+
/>
53+
);
54+
};

packages/eui/src/components/flyout/flyout.component.tsx

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ import {
6767
isEuiFlyoutSizeNamed,
6868
} from './const';
6969
import { useIsPushed } from './hooks';
70+
import { EuiFlyoutResizeButton } from './_flyout_resize_button';
71+
import { useEuiFlyoutResizable } from './use_flyout_resizable';
7072

7173
interface _EuiFlyoutComponentProps {
7274
onClose: (event: MouseEvent | TouchEvent | KeyboardEvent) => void;
@@ -76,6 +78,11 @@ interface _EuiFlyoutComponentProps {
7678
* @default m
7779
*/
7880
size?: EuiFlyoutSize | CSSProperties['width'];
81+
/**
82+
* Sets the minimum width of the panel.
83+
* Especially useful when set with `resizable = true`.
84+
*/
85+
minWidth?: number;
7986
/**
8087
* Sets the max-width of the panel,
8188
* set to `true` to use the default size,
@@ -166,6 +173,17 @@ interface _EuiFlyoutComponentProps {
166173
* Specify additional css selectors to include in the focus trap.
167174
*/
168175
includeSelectorInFocusTrap?: string[] | string;
176+
177+
/**
178+
* Whether the flyout should be resizable.
179+
* @default false
180+
*/
181+
resizable?: boolean;
182+
183+
/**
184+
* Optional callback that fires when the flyout is resized.
185+
*/
186+
onResize?: (width: number) => void;
169187
}
170188

171189
const defaultElement = 'div';
@@ -199,7 +217,7 @@ export const EuiFlyoutComponent = forwardRef(
199217
onClose,
200218
ownFocus = true,
201219
side = DEFAULT_SIDE,
202-
size = DEFAULT_SIZE,
220+
size: _size = DEFAULT_SIZE,
203221
paddingSize = DEFAULT_PADDING_SIZE,
204222
maxWidth = false,
205223
style,
@@ -213,6 +231,9 @@ export const EuiFlyoutComponent = forwardRef(
213231
includeSelectorInFocusTrap,
214232
'aria-describedby': _ariaDescribedBy,
215233
id,
234+
resizable = false,
235+
minWidth,
236+
onResize,
216237
...rest
217238
} = usePropsWithComponentDefaults('EuiFlyout', props);
218239

@@ -225,6 +246,20 @@ export const EuiFlyoutComponent = forwardRef(
225246
const internalParentFlyoutRef = useRef<HTMLDivElement>(null);
226247
const isPushed = useIsPushed({ type, pushMinBreakpoint });
227248

249+
const {
250+
onMouseDown: onMouseDownResizableButton,
251+
onKeyDown: onKeyDownResizableButton,
252+
size,
253+
setFlyoutRef,
254+
} = useEuiFlyoutResizable({
255+
enabled: resizable,
256+
minWidth,
257+
maxWidth: typeof maxWidth === 'number' ? maxWidth : 0,
258+
onResize,
259+
side,
260+
size: _size,
261+
});
262+
228263
/**
229264
* Setting up the refs on the actual flyout element in order to
230265
* accommodate for the `isPushed` state by adding padding to the body equal to the width of the element
@@ -236,6 +271,7 @@ export const EuiFlyoutComponent = forwardRef(
236271
setResizeRef,
237272
ref,
238273
internalParentFlyoutRef,
274+
setFlyoutRef,
239275
]);
240276
const { width } = useResizeObserver(isPushed ? resizeRef : null, 'width');
241277

@@ -534,6 +570,17 @@ export const EuiFlyoutComponent = forwardRef(
534570
side={side}
535571
/>
536572
)}
573+
{resizable && (
574+
<EuiFlyoutResizeButton
575+
type={type}
576+
side={side}
577+
ownFocus={ownFocus}
578+
isPushed={isPushed}
579+
onMouseDown={onMouseDownResizableButton}
580+
onTouchStart={onMouseDownResizableButton}
581+
onKeyDown={onKeyDownResizableButton}
582+
/>
583+
)}
537584
{children}
538585
</Element>
539586
</EuiFocusTrap>

0 commit comments

Comments
 (0)