-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Dashboard] New layout engine #174132
[Dashboard] New layout engine #174132
Changes from 31 commits
9bbe618
4b93780
bdf52b1
44080d1
140bc32
9b368d9
b6110df
227e722
1c6e194
107d06f
e7895a5
5f56371
81e23a5
69453e6
2a8a444
8fb43d4
4cf12c5
00319ee
a59c605
327e0f5
cf67c34
3d71fad
4e2d80d
2f162b5
1bccdb3
5e182eb
f8b4de5
a88dbfe
2158737
eed959d
e3d1d0e
75a4e51
48fcdc4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Grid Example | ||
|
||
This temporary example plugin will be used to build out the new layout engine for Dashboards. | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"type": "plugin", | ||
"id": "@kbn/grid-example-plugin", | ||
"owner": "@elastic/kibana-presentation", | ||
"description": "Temporary example app used to build out the new Dashboard layout system", | ||
"plugin": { | ||
"id": "gridExample", | ||
"server": false, | ||
"browser": true, | ||
"requiredPlugins": ["developerExamples"], | ||
"requiredBundles": [] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* | ||
* 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 React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import { GridLayout, type GridLayoutData } from '@kbn/grid-layout'; | ||
import { AppMountParameters } from '@kbn/core-application-browser'; | ||
import { EuiPageTemplate, EuiProvider } from '@elastic/eui'; | ||
|
||
export const GridExample = () => { | ||
return ( | ||
<EuiProvider> | ||
<EuiPageTemplate offset={0} restrictWidth={false}> | ||
<EuiPageTemplate.Header iconType={'dashboardApp'} pageTitle="Grid Layout Example" /> | ||
<EuiPageTemplate.Section> | ||
<GridLayout | ||
ThomThomson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
renderPanelContents={(id) => { | ||
return <div style={{ padding: 8 }}>{id}</div>; | ||
}} | ||
getCreationOptions={() => { | ||
const initialLayout: GridLayoutData = [ | ||
{ | ||
title: 'Large section', | ||
isCollapsed: false, | ||
panels: { | ||
panel1: { column: 0, row: 0, width: 12, height: 6, id: 'panel1' }, | ||
panel2: { column: 0, row: 6, width: 8, height: 4, id: 'panel2' }, | ||
panel3: { column: 8, row: 6, width: 12, height: 4, id: 'panel3' }, | ||
panel4: { column: 0, row: 10, width: 48, height: 4, id: 'panel4' }, | ||
panel5: { column: 12, row: 0, width: 36, height: 6, id: 'panel5' }, | ||
panel6: { column: 24, row: 6, width: 24, height: 4, id: 'panel6' }, | ||
panel7: { column: 20, row: 6, width: 4, height: 2, id: 'panel7' }, | ||
panel8: { column: 20, row: 8, width: 4, height: 2, id: 'panel8' }, | ||
}, | ||
}, | ||
{ | ||
title: 'Small section', | ||
isCollapsed: false, | ||
panels: { panel9: { column: 0, row: 0, width: 12, height: 6, id: 'panel9' } }, | ||
}, | ||
{ | ||
title: 'Another small section', | ||
isCollapsed: false, | ||
panels: { panel10: { column: 24, row: 0, width: 12, height: 6, id: 'panel10' } }, | ||
}, | ||
]; | ||
|
||
return { | ||
gridSettings: { gutterSize: 8, rowHeight: 26, columnCount: 48 }, | ||
initialLayout, | ||
}; | ||
}} | ||
/> | ||
</EuiPageTemplate.Section> | ||
</EuiPageTemplate> | ||
</EuiProvider> | ||
); | ||
}; | ||
|
||
export const renderGridExampleApp = (element: AppMountParameters['element']) => { | ||
ReactDOM.render(<GridExample />, element); | ||
|
||
return () => ReactDOM.unmountComponentAtNode(element); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* | ||
* 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 { GridExamplePlugin } from './plugin'; | ||
|
||
export const plugin = () => new GridExamplePlugin(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* 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 { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; | ||
import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public'; | ||
|
||
export const GRID_EXAMPLE_APP_ID = 'gridExample'; | ||
const gridExampleTitle = 'Grid Example'; | ||
|
||
interface GridExamplePluginSetupDependencies { | ||
developerExamples: DeveloperExamplesSetup; | ||
} | ||
|
||
export class GridExamplePlugin | ||
implements Plugin<void, void, GridExamplePluginSetupDependencies, {}> | ||
{ | ||
public setup(core: CoreSetup<{}>, { developerExamples }: GridExamplePluginSetupDependencies) { | ||
core.application.register({ | ||
id: GRID_EXAMPLE_APP_ID, | ||
title: gridExampleTitle, | ||
visibleIn: [], | ||
async mount(params: AppMountParameters) { | ||
const { renderGridExampleApp } = await import('./app'); | ||
return renderGridExampleApp(params.element); | ||
}, | ||
}); | ||
developerExamples.register({ | ||
appId: GRID_EXAMPLE_APP_ID, | ||
title: gridExampleTitle, | ||
description: `This temporary example plugin will be used to build out the new layout engine for Dashboards.`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. again, lets change wording to call it a playground for the layout engine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in 75a4e51 |
||
}); | ||
} | ||
|
||
public start(core: CoreStart, deps: {}) {} | ||
|
||
public stop() {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"extends": "../../tsconfig.base.json", | ||
"compilerOptions": { | ||
"outDir": "target/types" | ||
}, | ||
"include": ["index.ts", "public/**/*.ts", "public/**/*.tsx", "../../typings/**/*"], | ||
"exclude": ["target/**/*"], | ||
"kbn_references": [ | ||
"@kbn/grid-layout", | ||
"@kbn/core-application-browser", | ||
"@kbn/core", | ||
"@kbn/developer-examples-plugin", | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# @kbn/grid-layout | ||
|
||
Contains a simple drag and drop layout engine for Kibana Dashboards. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/* | ||
* 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, 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 { GridRow } from './grid_row'; | ||
import { GridLayoutData, GridSettings } from './types'; | ||
import { useGridLayoutEvents } from './use_grid_layout_events'; | ||
import { useGridLayoutState } from './use_grid_layout_state'; | ||
|
||
export const GridLayout = ({ | ||
getCreationOptions, | ||
renderPanelContents, | ||
}: { | ||
getCreationOptions: () => { initialLayout: GridLayoutData; gridSettings: GridSettings }; | ||
renderPanelContents: (panelId: string) => React.ReactNode; | ||
}) => { | ||
const { gridLayoutStateManager, gridSizeRef } = useGridLayoutState({ | ||
getCreationOptions, | ||
}); | ||
useGridLayoutEvents({ gridLayoutStateManager }); | ||
|
||
const [gridLayout, runtimeSettings, interactionEvent] = useBatchedPublishingSubjects( | ||
gridLayoutStateManager.gridLayout$, | ||
gridLayoutStateManager.runtimeSettings$, | ||
gridLayoutStateManager.interactionEvent$ | ||
); | ||
|
||
return ( | ||
<div ref={gridSizeRef}> | ||
{gridLayout.map((rowData, rowIndex) => { | ||
return ( | ||
<GridRow | ||
key={rowIndex} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about using rowData.title as key since index is not a recommended key? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch! Addressed in 75a4e51 |
||
rowData={rowData} | ||
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> | ||
<div | ||
css={css` | ||
top: 0; | ||
left: 0; | ||
width: 100vw; | ||
height: 100vh; | ||
position: fixed; | ||
overflow: hidden; | ||
pointer-events: none; | ||
z-index: ${euiThemeVars.euiZModal}; | ||
`} | ||
> | ||
<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> | ||
</div> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets not make this temporary. It would be nice to have a playground for the layout engine outside of dashboard. Plus, its a good learning resource without all the noise of dashboard implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 75a4e51