Skip to content

Commit

Permalink
Merge branch 'main' into roles/details/spaces-modal
Browse files Browse the repository at this point in the history
  • Loading branch information
tsullivan committed Aug 27, 2024
2 parents 1bf9623 + 8d5d345 commit 3f4c279
Show file tree
Hide file tree
Showing 116 changed files with 1,292 additions and 448 deletions.
1 change: 1 addition & 0 deletions .i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"presentationPanel": "src/plugins/presentation_panel",
"embeddableExamples": "examples/embeddable_examples",
"esQuery": "packages/kbn-es-query/src",
"kbnGridLayout": "packages/kbn-grid-layout",
"esUi": "src/plugins/es_ui_shared",
"expandableFlyout": "packages/kbn-expandable-flyout",
"expressionError": "src/plugins/expression_error",
Expand Down
6 changes: 3 additions & 3 deletions examples/grid_example/public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { EuiPageTemplate, EuiProvider } from '@elastic/eui';
export const GridExample = () => {
return (
<EuiProvider>
<EuiPageTemplate offset={0} restrictWidth={false}>
<EuiPageTemplate grow={false} offset={0} restrictWidth={false}>
<EuiPageTemplate.Header iconType={'dashboardApp'} pageTitle="Grid Layout Example" />
<EuiPageTemplate.Section>
<EuiPageTemplate.Section color="subdued">
<GridLayout
renderPanelContents={(id) => {
return <div style={{ padding: 8 }}>{id}</div>;
Expand All @@ -41,7 +41,7 @@ export const GridExample = () => {
{
title: 'Small section',
isCollapsed: false,
panels: { panel9: { column: 0, row: 0, width: 12, height: 6, id: 'panel9' } },
panels: { panel9: { column: 0, row: 0, width: 12, height: 16, id: 'panel9' } },
},
{
title: 'Another small section',
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1136,9 +1136,9 @@
"mime": "^2.4.4",
"mime-types": "^2.1.27",
"minimatch": "^3.1.2",
"moment": "^2.29.4",
"moment": "^2.30.1",
"moment-duration-format": "^2.3.2",
"moment-timezone": "^0.5.43",
"moment-timezone": "^0.5.45",
"monaco-editor": "^0.44.0",
"monaco-yaml": "^5.1.0",
"murmurhash": "^2.0.1",
Expand Down
56 changes: 56 additions & 0 deletions packages/kbn-grid-layout/grid/grid_height_smoother.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { css } from '@emotion/react';
import React, { PropsWithChildren, useEffect, useRef } from 'react';
import { combineLatest } from 'rxjs';
import { GridLayoutStateManager } from './types';

export const GridHeightSmoother = ({
children,
gridLayoutStateManager,
}: PropsWithChildren<{ gridLayoutStateManager: GridLayoutStateManager }>) => {
// set the parent div size directly to smooth out height changes.
const smoothHeightRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const subscription = combineLatest([
gridLayoutStateManager.gridDimensions$,
gridLayoutStateManager.interactionEvent$,
]).subscribe(([dimensions, interactionEvent]) => {
if (!smoothHeightRef.current) return;
if (!interactionEvent) {
smoothHeightRef.current.style.height = `${dimensions.height}px`;
return;
}

/**
* When the user is interacting with an element, the page can grow, but it cannot
* shrink. This is to stop a behaviour where the page would scroll up automatically
* making the panel shrink or grow unpredictably.
*/
smoothHeightRef.current.style.height = `${Math.max(
dimensions.height ?? 0,
smoothHeightRef.current.getBoundingClientRect().height
)}px`;
});
return () => subscription.unsubscribe();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<div
ref={smoothHeightRef}
css={css`
overflow-anchor: none;
transition: height 500ms linear;
`}
>
{children}
</div>
);
};
93 changes: 39 additions & 54 deletions packages/kbn-grid-layout/grid/grid_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
* Side Public License, v 1.
*/

import { EuiPortal, transparentize } from '@elastic/eui';
import { css } from '@emotion/react';
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import { euiThemeVars } from '@kbn/ui-theme';
import React from 'react';
import { GridHeightSmoother } from './grid_height_smoother';
import { GridOverlay } from './grid_overlay';
import { GridRow } from './grid_row';
import { GridLayoutData, GridSettings } from './types';
import { useGridLayoutEvents } from './use_grid_layout_events';
Expand All @@ -23,7 +22,7 @@ export const GridLayout = ({
getCreationOptions: () => { initialLayout: GridLayoutData; gridSettings: GridSettings };
renderPanelContents: (panelId: string) => React.ReactNode;
}) => {
const { gridLayoutStateManager, gridSizeRef } = useGridLayoutState({
const { gridLayoutStateManager, setDimensionsRef } = useGridLayoutState({
getCreationOptions,
});
useGridLayoutEvents({ gridLayoutStateManager });
Expand All @@ -35,58 +34,44 @@ export const GridLayout = ({
);

return (
<div ref={gridSizeRef}>
{gridLayout.map((rowData, rowIndex) => {
return (
<GridRow
rowData={rowData}
key={rowData.title}
rowIndex={rowIndex}
runtimeSettings={runtimeSettings}
activePanelId={interactionEvent?.id}
renderPanelContents={renderPanelContents}
targetRowIndex={interactionEvent?.targetRowIndex}
toggleIsCollapsed={() => {
const currentLayout = gridLayoutStateManager.gridLayout$.value;
currentLayout[rowIndex].isCollapsed = !currentLayout[rowIndex].isCollapsed;
gridLayoutStateManager.gridLayout$.next(currentLayout);
}}
setInteractionEvent={(nextInteractionEvent) => {
if (!nextInteractionEvent) {
gridLayoutStateManager.hideDragPreview();
}
gridLayoutStateManager.interactionEvent$.next(nextInteractionEvent);
}}
ref={(element) => (gridLayoutStateManager.rowRefs.current[rowIndex] = element)}
/>
);
})}
<EuiPortal>
<>
<GridHeightSmoother gridLayoutStateManager={gridLayoutStateManager}>
<div
css={css`
top: 0;
left: 0;
width: 100vw;
height: 100vh;
position: fixed;
overflow: hidden;
pointer-events: none;
z-index: ${euiThemeVars.euiZModal};
`}
ref={(divElement) => {
setDimensionsRef(divElement);
}}
>
<div
ref={gridLayoutStateManager.dragPreviewRef}
css={css`
pointer-events: none;
z-index: ${euiThemeVars.euiZModal};
border-radius: ${euiThemeVars.euiBorderRadius};
background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.2)};
transition: opacity 100ms linear;
position: absolute;
`}
/>
{gridLayout.map((rowData, rowIndex) => {
return (
<GridRow
rowData={rowData}
key={rowData.title}
rowIndex={rowIndex}
runtimeSettings={runtimeSettings}
activePanelId={interactionEvent?.id}
renderPanelContents={renderPanelContents}
targetRowIndex={interactionEvent?.targetRowIndex}
toggleIsCollapsed={() => {
const currentLayout = gridLayoutStateManager.gridLayout$.value;
currentLayout[rowIndex].isCollapsed = !currentLayout[rowIndex].isCollapsed;
gridLayoutStateManager.gridLayout$.next(currentLayout);
}}
setInteractionEvent={(nextInteractionEvent) => {
if (!nextInteractionEvent) {
gridLayoutStateManager.hideDragPreview();
}
gridLayoutStateManager.interactionEvent$.next(nextInteractionEvent);
}}
ref={(element) => (gridLayoutStateManager.rowRefs.current[rowIndex] = element)}
/>
);
})}
</div>
</EuiPortal>
</div>
</GridHeightSmoother>
<GridOverlay
interactionEvent={interactionEvent}
gridLayoutStateManager={gridLayoutStateManager}
/>
</>
);
};
134 changes: 134 additions & 0 deletions packages/kbn-grid-layout/grid/grid_overlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { EuiPortal, EuiText, transparentize } from '@elastic/eui';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { euiThemeVars } from '@kbn/ui-theme';
import React, { useRef, useState } from 'react';
import { GridLayoutStateManager, PanelInteractionEvent } from './types';

type ScrollDirection = 'up' | 'down';

const scrollLabels: { [key in ScrollDirection]: string } = {
up: i18n.translate('kbnGridLayout.overlays.scrollUpLabel', { defaultMessage: 'Scroll up' }),
down: i18n.translate('kbnGridLayout.overlays.scrollDownLabel', { defaultMessage: 'Scroll down' }),
};

const scrollOnInterval = (direction: ScrollDirection) => {
const interval = setInterval(() => {
window.scroll({
top: window.scrollY + (direction === 'down' ? 50 : -50),
behavior: 'smooth',
});
}, 100);
return interval;
};

const ScrollOnHover = ({ direction, hide }: { hide: boolean; direction: ScrollDirection }) => {
const [isActive, setIsActive] = useState(false);
const scrollInterval = useRef<NodeJS.Timeout | null>(null);
const stopScrollInterval = () => {
if (scrollInterval.current) {
clearInterval(scrollInterval.current);
}
};

return (
<div
onMouseEnter={() => {
setIsActive(true);
scrollInterval.current = scrollOnInterval(direction);
}}
onMouseLeave={() => {
setIsActive(false);
stopScrollInterval();
}}
css={css`
width: 100%;
position: fixed;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
opacity: ${hide ? 0 : 1};
transition: opacity 100ms linear;
padding: ${euiThemeVars.euiSizeM};
${direction === 'down' ? 'bottom: 0;' : 'top: 0;'}
`}
>
{direction === 'up' && (
<div
css={css`
height: 96px;
`}
/>
)}
<div
css={css`
display: flex;
width: fit-content;
align-items: center;
background-color: ${isActive
? euiThemeVars.euiColorSuccess
: euiThemeVars.euiColorEmptyShade};
height: ${euiThemeVars.euiButtonHeight};
line-height: ${euiThemeVars.euiButtonHeight};
border-radius: ${euiThemeVars.euiButtonHeight};
outline: ${isActive ? 'none' : euiThemeVars.euiBorderThin};
transition: background-color 100ms linear, color 100ms linear;
padding: 0 ${euiThemeVars.euiSizeL} 0 ${euiThemeVars.euiSizeL};
color: ${isActive ? euiThemeVars.euiColorEmptyShade : euiThemeVars.euiTextColor};
`}
>
<EuiText size="m">
<strong>{scrollLabels[direction]}</strong>
</EuiText>
</div>
</div>
);
};

export const GridOverlay = ({
interactionEvent,
gridLayoutStateManager,
}: {
interactionEvent?: PanelInteractionEvent;
gridLayoutStateManager: GridLayoutStateManager;
}) => {
return (
<EuiPortal>
<div
css={css`
top: 0;
left: 0;
width: 100vw;
height: 100vh;
position: fixed;
overflow: hidden;
z-index: ${euiThemeVars.euiZModal};
pointer-events: ${interactionEvent ? 'unset' : 'none'};
`}
>
<ScrollOnHover hide={!interactionEvent} direction="up" />
<ScrollOnHover hide={!interactionEvent} direction="down" />
<div
ref={gridLayoutStateManager.dragPreviewRef}
css={css`
pointer-events: none;
z-index: ${euiThemeVars.euiZModal};
border-radius: ${euiThemeVars.euiBorderRadius};
background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.2)};
transition: opacity 100ms linear;
position: absolute;
`}
/>
</div>
</EuiPortal>
);
};
Loading

0 comments on commit 3f4c279

Please sign in to comment.