Skip to content

Commit 3f4c279

Browse files
committed
Merge branch 'main' into roles/details/spaces-modal
2 parents 1bf9623 + 8d5d345 commit 3f4c279

File tree

116 files changed

+1292
-448
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+1292
-448
lines changed

Diff for: .i18nrc.json

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"presentationPanel": "src/plugins/presentation_panel",
3434
"embeddableExamples": "examples/embeddable_examples",
3535
"esQuery": "packages/kbn-es-query/src",
36+
"kbnGridLayout": "packages/kbn-grid-layout",
3637
"esUi": "src/plugins/es_ui_shared",
3738
"expandableFlyout": "packages/kbn-expandable-flyout",
3839
"expressionError": "src/plugins/expression_error",

Diff for: examples/grid_example/public/app.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import { EuiPageTemplate, EuiProvider } from '@elastic/eui';
1515
export const GridExample = () => {
1616
return (
1717
<EuiProvider>
18-
<EuiPageTemplate offset={0} restrictWidth={false}>
18+
<EuiPageTemplate grow={false} offset={0} restrictWidth={false}>
1919
<EuiPageTemplate.Header iconType={'dashboardApp'} pageTitle="Grid Layout Example" />
20-
<EuiPageTemplate.Section>
20+
<EuiPageTemplate.Section color="subdued">
2121
<GridLayout
2222
renderPanelContents={(id) => {
2323
return <div style={{ padding: 8 }}>{id}</div>;
@@ -41,7 +41,7 @@ export const GridExample = () => {
4141
{
4242
title: 'Small section',
4343
isCollapsed: false,
44-
panels: { panel9: { column: 0, row: 0, width: 12, height: 6, id: 'panel9' } },
44+
panels: { panel9: { column: 0, row: 0, width: 12, height: 16, id: 'panel9' } },
4545
},
4646
{
4747
title: 'Another small section',

Diff for: package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1136,9 +1136,9 @@
11361136
"mime": "^2.4.4",
11371137
"mime-types": "^2.1.27",
11381138
"minimatch": "^3.1.2",
1139-
"moment": "^2.29.4",
1139+
"moment": "^2.30.1",
11401140
"moment-duration-format": "^2.3.2",
1141-
"moment-timezone": "^0.5.43",
1141+
"moment-timezone": "^0.5.45",
11421142
"monaco-editor": "^0.44.0",
11431143
"monaco-yaml": "^5.1.0",
11441144
"murmurhash": "^2.0.1",
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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 { css } from '@emotion/react';
10+
import React, { PropsWithChildren, useEffect, useRef } from 'react';
11+
import { combineLatest } from 'rxjs';
12+
import { GridLayoutStateManager } from './types';
13+
14+
export const GridHeightSmoother = ({
15+
children,
16+
gridLayoutStateManager,
17+
}: PropsWithChildren<{ gridLayoutStateManager: GridLayoutStateManager }>) => {
18+
// set the parent div size directly to smooth out height changes.
19+
const smoothHeightRef = useRef<HTMLDivElement | null>(null);
20+
useEffect(() => {
21+
const subscription = combineLatest([
22+
gridLayoutStateManager.gridDimensions$,
23+
gridLayoutStateManager.interactionEvent$,
24+
]).subscribe(([dimensions, interactionEvent]) => {
25+
if (!smoothHeightRef.current) return;
26+
if (!interactionEvent) {
27+
smoothHeightRef.current.style.height = `${dimensions.height}px`;
28+
return;
29+
}
30+
31+
/**
32+
* When the user is interacting with an element, the page can grow, but it cannot
33+
* shrink. This is to stop a behaviour where the page would scroll up automatically
34+
* making the panel shrink or grow unpredictably.
35+
*/
36+
smoothHeightRef.current.style.height = `${Math.max(
37+
dimensions.height ?? 0,
38+
smoothHeightRef.current.getBoundingClientRect().height
39+
)}px`;
40+
});
41+
return () => subscription.unsubscribe();
42+
// eslint-disable-next-line react-hooks/exhaustive-deps
43+
}, []);
44+
45+
return (
46+
<div
47+
ref={smoothHeightRef}
48+
css={css`
49+
overflow-anchor: none;
50+
transition: height 500ms linear;
51+
`}
52+
>
53+
{children}
54+
</div>
55+
);
56+
};

Diff for: packages/kbn-grid-layout/grid/grid_layout.tsx

+39-54
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
* Side Public License, v 1.
77
*/
88

9-
import { EuiPortal, transparentize } from '@elastic/eui';
10-
import { css } from '@emotion/react';
119
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
12-
import { euiThemeVars } from '@kbn/ui-theme';
1310
import React from 'react';
11+
import { GridHeightSmoother } from './grid_height_smoother';
12+
import { GridOverlay } from './grid_overlay';
1413
import { GridRow } from './grid_row';
1514
import { GridLayoutData, GridSettings } from './types';
1615
import { useGridLayoutEvents } from './use_grid_layout_events';
@@ -23,7 +22,7 @@ export const GridLayout = ({
2322
getCreationOptions: () => { initialLayout: GridLayoutData; gridSettings: GridSettings };
2423
renderPanelContents: (panelId: string) => React.ReactNode;
2524
}) => {
26-
const { gridLayoutStateManager, gridSizeRef } = useGridLayoutState({
25+
const { gridLayoutStateManager, setDimensionsRef } = useGridLayoutState({
2726
getCreationOptions,
2827
});
2928
useGridLayoutEvents({ gridLayoutStateManager });
@@ -35,58 +34,44 @@ export const GridLayout = ({
3534
);
3635

3736
return (
38-
<div ref={gridSizeRef}>
39-
{gridLayout.map((rowData, rowIndex) => {
40-
return (
41-
<GridRow
42-
rowData={rowData}
43-
key={rowData.title}
44-
rowIndex={rowIndex}
45-
runtimeSettings={runtimeSettings}
46-
activePanelId={interactionEvent?.id}
47-
renderPanelContents={renderPanelContents}
48-
targetRowIndex={interactionEvent?.targetRowIndex}
49-
toggleIsCollapsed={() => {
50-
const currentLayout = gridLayoutStateManager.gridLayout$.value;
51-
currentLayout[rowIndex].isCollapsed = !currentLayout[rowIndex].isCollapsed;
52-
gridLayoutStateManager.gridLayout$.next(currentLayout);
53-
}}
54-
setInteractionEvent={(nextInteractionEvent) => {
55-
if (!nextInteractionEvent) {
56-
gridLayoutStateManager.hideDragPreview();
57-
}
58-
gridLayoutStateManager.interactionEvent$.next(nextInteractionEvent);
59-
}}
60-
ref={(element) => (gridLayoutStateManager.rowRefs.current[rowIndex] = element)}
61-
/>
62-
);
63-
})}
64-
<EuiPortal>
37+
<>
38+
<GridHeightSmoother gridLayoutStateManager={gridLayoutStateManager}>
6539
<div
66-
css={css`
67-
top: 0;
68-
left: 0;
69-
width: 100vw;
70-
height: 100vh;
71-
position: fixed;
72-
overflow: hidden;
73-
pointer-events: none;
74-
z-index: ${euiThemeVars.euiZModal};
75-
`}
40+
ref={(divElement) => {
41+
setDimensionsRef(divElement);
42+
}}
7643
>
77-
<div
78-
ref={gridLayoutStateManager.dragPreviewRef}
79-
css={css`
80-
pointer-events: none;
81-
z-index: ${euiThemeVars.euiZModal};
82-
border-radius: ${euiThemeVars.euiBorderRadius};
83-
background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.2)};
84-
transition: opacity 100ms linear;
85-
position: absolute;
86-
`}
87-
/>
44+
{gridLayout.map((rowData, rowIndex) => {
45+
return (
46+
<GridRow
47+
rowData={rowData}
48+
key={rowData.title}
49+
rowIndex={rowIndex}
50+
runtimeSettings={runtimeSettings}
51+
activePanelId={interactionEvent?.id}
52+
renderPanelContents={renderPanelContents}
53+
targetRowIndex={interactionEvent?.targetRowIndex}
54+
toggleIsCollapsed={() => {
55+
const currentLayout = gridLayoutStateManager.gridLayout$.value;
56+
currentLayout[rowIndex].isCollapsed = !currentLayout[rowIndex].isCollapsed;
57+
gridLayoutStateManager.gridLayout$.next(currentLayout);
58+
}}
59+
setInteractionEvent={(nextInteractionEvent) => {
60+
if (!nextInteractionEvent) {
61+
gridLayoutStateManager.hideDragPreview();
62+
}
63+
gridLayoutStateManager.interactionEvent$.next(nextInteractionEvent);
64+
}}
65+
ref={(element) => (gridLayoutStateManager.rowRefs.current[rowIndex] = element)}
66+
/>
67+
);
68+
})}
8869
</div>
89-
</EuiPortal>
90-
</div>
70+
</GridHeightSmoother>
71+
<GridOverlay
72+
interactionEvent={interactionEvent}
73+
gridLayoutStateManager={gridLayoutStateManager}
74+
/>
75+
</>
9176
);
9277
};

Diff for: packages/kbn-grid-layout/grid/grid_overlay.tsx

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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 { EuiPortal, EuiText, transparentize } from '@elastic/eui';
10+
import { css } from '@emotion/react';
11+
import { i18n } from '@kbn/i18n';
12+
import { euiThemeVars } from '@kbn/ui-theme';
13+
import React, { useRef, useState } from 'react';
14+
import { GridLayoutStateManager, PanelInteractionEvent } from './types';
15+
16+
type ScrollDirection = 'up' | 'down';
17+
18+
const scrollLabels: { [key in ScrollDirection]: string } = {
19+
up: i18n.translate('kbnGridLayout.overlays.scrollUpLabel', { defaultMessage: 'Scroll up' }),
20+
down: i18n.translate('kbnGridLayout.overlays.scrollDownLabel', { defaultMessage: 'Scroll down' }),
21+
};
22+
23+
const scrollOnInterval = (direction: ScrollDirection) => {
24+
const interval = setInterval(() => {
25+
window.scroll({
26+
top: window.scrollY + (direction === 'down' ? 50 : -50),
27+
behavior: 'smooth',
28+
});
29+
}, 100);
30+
return interval;
31+
};
32+
33+
const ScrollOnHover = ({ direction, hide }: { hide: boolean; direction: ScrollDirection }) => {
34+
const [isActive, setIsActive] = useState(false);
35+
const scrollInterval = useRef<NodeJS.Timeout | null>(null);
36+
const stopScrollInterval = () => {
37+
if (scrollInterval.current) {
38+
clearInterval(scrollInterval.current);
39+
}
40+
};
41+
42+
return (
43+
<div
44+
onMouseEnter={() => {
45+
setIsActive(true);
46+
scrollInterval.current = scrollOnInterval(direction);
47+
}}
48+
onMouseLeave={() => {
49+
setIsActive(false);
50+
stopScrollInterval();
51+
}}
52+
css={css`
53+
width: 100%;
54+
position: fixed;
55+
display: flex;
56+
align-items: center;
57+
flex-direction: column;
58+
justify-content: center;
59+
opacity: ${hide ? 0 : 1};
60+
transition: opacity 100ms linear;
61+
padding: ${euiThemeVars.euiSizeM};
62+
${direction === 'down' ? 'bottom: 0;' : 'top: 0;'}
63+
`}
64+
>
65+
{direction === 'up' && (
66+
<div
67+
css={css`
68+
height: 96px;
69+
`}
70+
/>
71+
)}
72+
<div
73+
css={css`
74+
display: flex;
75+
width: fit-content;
76+
align-items: center;
77+
background-color: ${isActive
78+
? euiThemeVars.euiColorSuccess
79+
: euiThemeVars.euiColorEmptyShade};
80+
height: ${euiThemeVars.euiButtonHeight};
81+
line-height: ${euiThemeVars.euiButtonHeight};
82+
border-radius: ${euiThemeVars.euiButtonHeight};
83+
outline: ${isActive ? 'none' : euiThemeVars.euiBorderThin};
84+
transition: background-color 100ms linear, color 100ms linear;
85+
padding: 0 ${euiThemeVars.euiSizeL} 0 ${euiThemeVars.euiSizeL};
86+
color: ${isActive ? euiThemeVars.euiColorEmptyShade : euiThemeVars.euiTextColor};
87+
`}
88+
>
89+
<EuiText size="m">
90+
<strong>{scrollLabels[direction]}</strong>
91+
</EuiText>
92+
</div>
93+
</div>
94+
);
95+
};
96+
97+
export const GridOverlay = ({
98+
interactionEvent,
99+
gridLayoutStateManager,
100+
}: {
101+
interactionEvent?: PanelInteractionEvent;
102+
gridLayoutStateManager: GridLayoutStateManager;
103+
}) => {
104+
return (
105+
<EuiPortal>
106+
<div
107+
css={css`
108+
top: 0;
109+
left: 0;
110+
width: 100vw;
111+
height: 100vh;
112+
position: fixed;
113+
overflow: hidden;
114+
z-index: ${euiThemeVars.euiZModal};
115+
pointer-events: ${interactionEvent ? 'unset' : 'none'};
116+
`}
117+
>
118+
<ScrollOnHover hide={!interactionEvent} direction="up" />
119+
<ScrollOnHover hide={!interactionEvent} direction="down" />
120+
<div
121+
ref={gridLayoutStateManager.dragPreviewRef}
122+
css={css`
123+
pointer-events: none;
124+
z-index: ${euiThemeVars.euiZModal};
125+
border-radius: ${euiThemeVars.euiBorderRadius};
126+
background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.2)};
127+
transition: opacity 100ms linear;
128+
position: absolute;
129+
`}
130+
/>
131+
</div>
132+
</EuiPortal>
133+
);
134+
};

0 commit comments

Comments
 (0)