66 * Side Public License, v 1.
77 */
88
9- import React , { useEffect , useState } from 'react' ;
9+ import { useCallback , useEffect , useMemo , useState } from 'react' ;
1010import { useEuiTheme } from '../../../services' ;
1111import { setLayoutMode } from './actions' ;
1212import {
@@ -37,18 +37,23 @@ export const useApplyFlyoutLayoutMode = () => {
3737
3838 const parentWidth = useFlyoutWidth ( parentFlyoutId ) ;
3939 const childWidth = useFlyoutWidth ( childFlyoutId ) ;
40+ const hasFlyouts = Boolean ( parentFlyoutId ) ;
4041
4142 const [ windowWidth , setWindowWidth ] = useState (
4243 typeof window !== 'undefined' ? window . innerWidth : Infinity
4344 ) ;
4445
45- const setMode = React . useCallback (
46+ // Extract specific context values
47+ const dispatch = context ?. dispatch ;
48+ const currentLayoutMode = context ?. state ?. layoutMode ;
49+
50+ const setMode = useCallback (
4651 ( layoutMode : EuiFlyoutLayoutMode ) => {
47- if ( context ?. dispatch && layoutMode !== context . state . layoutMode ) {
48- context . dispatch ( setLayoutMode ( layoutMode ) ) ;
52+ if ( dispatch ) {
53+ dispatch ( setLayoutMode ( layoutMode ) ) ;
4954 }
5055 } ,
51- [ context ]
56+ [ dispatch ]
5257 ) ;
5358
5459 useEffect ( ( ) => {
@@ -75,13 +80,13 @@ export const useApplyFlyoutLayoutMode = () => {
7580 } ;
7681 } , [ ] ) ;
7782
78- useEffect ( ( ) => {
79- if ( ! context ) {
80- return ;
83+ // Calculate the desired layout mode
84+ const desiredLayoutMode = useMemo ( ( ) => {
85+ // Skip calculation if no flyouts open
86+ if ( ! hasFlyouts ) {
87+ return null ;
8188 }
8289
83- const currentLayoutMode = context . state . layoutMode ;
84-
8590 // Thresholds to prevent thrashing near the breakpoint.
8691 const THRESHOLD_TO_SIDE_BY_SIDE = 85 ;
8792 const THRESHOLD_TO_STACKED = 95 ;
@@ -92,16 +97,11 @@ export const useApplyFlyoutLayoutMode = () => {
9297 // `composeFlyoutSizing` in `flyout.styles.ts` multiplied
9398 // by 2 (open flyouts side-by-side).
9499 if ( windowWidth < Math . round ( euiTheme . breakpoint . s * 1.4 ) ) {
95- if ( currentLayoutMode !== LAYOUT_MODE_STACKED ) {
96- setMode ( LAYOUT_MODE_STACKED ) ;
97- }
98- return ;
100+ return LAYOUT_MODE_STACKED ;
99101 }
100102
101103 if ( ! childFlyoutId ) {
102- if ( currentLayoutMode !== LAYOUT_MODE_SIDE_BY_SIDE )
103- setMode ( LAYOUT_MODE_SIDE_BY_SIDE ) ;
104- return ;
104+ return LAYOUT_MODE_SIDE_BY_SIDE ;
105105 }
106106
107107 let parentWidthValue = parentWidth ;
@@ -116,54 +116,49 @@ export const useApplyFlyoutLayoutMode = () => {
116116 }
117117
118118 if ( ! parentWidthValue || ! childWidthValue ) {
119- if ( currentLayoutMode !== LAYOUT_MODE_SIDE_BY_SIDE )
120- setMode ( LAYOUT_MODE_SIDE_BY_SIDE ) ;
121- return ;
119+ return LAYOUT_MODE_SIDE_BY_SIDE ;
122120 }
123121
124122 const combinedWidth = parentWidthValue + childWidthValue ;
125123 const combinedWidthPercentage = ( combinedWidth / windowWidth ) * 100 ;
126- let newLayoutMode : EuiFlyoutLayoutMode ;
127124
128125 // Handle fill size flyouts: keep layout as side-by-side when fill flyout is present
129126 // This allows fill flyouts to dynamically calculate their width based on the other in the pair
130127 if ( parentFlyout ?. size === 'fill' || childFlyout ?. size === 'fill' ) {
131128 // For fill flyouts, we want to maintain side-by-side layout to enable dynamic width calculation
132129 // Only stack if the viewport is too small (below the small breakpoint)
133130 if ( windowWidth >= Math . round ( euiTheme . breakpoint . s * 1.4 ) ) {
134- if ( currentLayoutMode !== LAYOUT_MODE_SIDE_BY_SIDE ) {
135- setMode ( LAYOUT_MODE_SIDE_BY_SIDE ) ;
136- }
137- return ;
131+ return LAYOUT_MODE_SIDE_BY_SIDE ;
138132 }
139133 }
140134
141135 if ( currentLayoutMode === LAYOUT_MODE_STACKED ) {
142- newLayoutMode =
143- combinedWidthPercentage <= THRESHOLD_TO_SIDE_BY_SIDE
144- ? LAYOUT_MODE_SIDE_BY_SIDE
145- : LAYOUT_MODE_STACKED ;
136+ return combinedWidthPercentage <= THRESHOLD_TO_SIDE_BY_SIDE
137+ ? LAYOUT_MODE_SIDE_BY_SIDE
138+ : LAYOUT_MODE_STACKED ;
146139 } else {
147- newLayoutMode =
148- combinedWidthPercentage >= THRESHOLD_TO_STACKED
149- ? LAYOUT_MODE_STACKED
150- : LAYOUT_MODE_SIDE_BY_SIDE ;
151- }
152-
153- if ( currentLayoutMode !== newLayoutMode ) {
154- setMode ( newLayoutMode ) ;
140+ return combinedWidthPercentage >= THRESHOLD_TO_STACKED
141+ ? LAYOUT_MODE_STACKED
142+ : LAYOUT_MODE_SIDE_BY_SIDE ;
155143 }
156144 } , [
145+ hasFlyouts ,
157146 windowWidth ,
158- context ,
147+ euiTheme ,
148+ childFlyoutId ,
159149 parentWidth ,
160- setMode ,
161150 childWidth ,
162- childFlyoutId ,
163151 parentFlyout ?. size ,
164152 childFlyout ?. size ,
165- euiTheme ,
153+ currentLayoutMode ,
166154 ] ) ;
155+
156+ // Apply the desired layout mode
157+ useEffect ( ( ) => {
158+ if ( desiredLayoutMode && currentLayoutMode !== desiredLayoutMode ) {
159+ setMode ( desiredLayoutMode ) ;
160+ }
161+ } , [ desiredLayoutMode , currentLayoutMode , setMode ] ) ;
167162} ;
168163
169164/** Convert a flyout `size` value to a pixel width using theme breakpoints. */
0 commit comments