mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Workspace Chrome] Bootstrap new grid layout components (#223890)
This is largely based of @clintandrewhall's work that he extracted from the new workspace layout poc. These components are a decent starting point for [new grid layout ](https://github.com/elastic/kibana-team/issues/1581) and I [validated](https://github.com/elastic/kibana/pull/223021) that the layout mostly works for Kibana (fixing a couple of edge cases) I believe the components are ready to be merged into the main branch to make future reviews easier: Bootstraps a new `@kbn/core-chrome-layout-components` package to provide composable React primitives for Kibana’s Chrome layout, including region components, a debug overlay, Storybook stories, and initial docs. - Adds layout region components (Banner, Header, Navigation, Sidebar, Panels, Application, Footer) and a debug overlay with Emotion styling. - Provides a README with usage examples (Storybook-driven) and API documentation. `yarn storybook sharedux`  --------- Co-authored-by: Clint Andrew Hall <clint@clintandrewhall.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
aac08931d1
commit
ada7278048
44 changed files with 1719 additions and 0 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -125,6 +125,7 @@ src/core/packages/capabilities/server-mocks @elastic/kibana-core
|
|||
src/core/packages/chrome/browser @elastic/appex-sharedux
|
||||
src/core/packages/chrome/browser-internal @elastic/appex-sharedux
|
||||
src/core/packages/chrome/browser-mocks @elastic/appex-sharedux
|
||||
src/core/packages/chrome/layout/core-chrome-layout-components @elastic/appex-sharedux
|
||||
src/core/packages/config/server-internal @elastic/kibana-core
|
||||
src/core/packages/custom-branding/browser @elastic/appex-sharedux
|
||||
src/core/packages/custom-branding/browser-internal @elastic/appex-sharedux
|
||||
|
|
|
@ -283,6 +283,7 @@
|
|||
"@kbn/core-capabilities-server-internal": "link:src/core/packages/capabilities/server-internal",
|
||||
"@kbn/core-chrome-browser": "link:src/core/packages/chrome/browser",
|
||||
"@kbn/core-chrome-browser-internal": "link:src/core/packages/chrome/browser-internal",
|
||||
"@kbn/core-chrome-layout-components": "link:src/core/packages/chrome/layout/core-chrome-layout-components",
|
||||
"@kbn/core-config-server-internal": "link:src/core/packages/config/server-internal",
|
||||
"@kbn/core-custom-branding-browser": "link:src/core/packages/custom-branding/browser",
|
||||
"@kbn/core-custom-branding-browser-internal": "link:src/core/packages/custom-branding/browser-internal",
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
# @kbn/core-chrome-layout-components
|
||||
|
||||
Composable React layout primitives for Kibana's Chrome application shell. Provides a modular, flexible, and accessible layout system using CSS Grid and Emotion styling.
|
||||
|
||||
## Features
|
||||
|
||||
- Modular layout regions: Header, Footer, Navigation, Sidebar, Banner, Navigation Panel, Sidebar Panel, Application content
|
||||
- Customizable dimensions for each region via context
|
||||
- Slot-based API: pass React nodes or render functions for each region
|
||||
- Responsive and accessible by design
|
||||
- Foundation for Kibana's main UI layout
|
||||
|
||||
## API
|
||||
|
||||
### Components
|
||||
|
||||
- `ChromeLayout`: Main layout component, accepts slot props for each region
|
||||
- `LayoutConfigProvider`: Context provider for layout dimensions
|
||||
|
||||
### Slot Props
|
||||
|
||||
All regions are passed as props to `ChromeLayout` Component. Each slot can be a React node or a function that receives the current layout state.
|
||||
|
||||
Available slots:
|
||||
- `header`
|
||||
- `footer`
|
||||
- `navigation`
|
||||
- `navigationPanel`
|
||||
- `sidebar`
|
||||
- `sidebarPanel`
|
||||
- `banner`
|
||||
- `children` (application content)
|
||||
|
||||
### Layout Configuration
|
||||
|
||||
Wrap your layout in a `LayoutConfigProvider` to set region sizes:
|
||||
|
||||
```tsx
|
||||
import {
|
||||
ChromeLayout,
|
||||
ChromeLayoutConfigProvider,
|
||||
} from '@kbn/core-chrome-layout-components';
|
||||
|
||||
<ChromeLayoutConfigProvider
|
||||
value={{
|
||||
bannerHeight: 32,
|
||||
headerHeight: 48,
|
||||
footerHeight: 24,
|
||||
navigationWidth: 200,
|
||||
navigationPanelWidth: 240,
|
||||
sidebarWidth: 300,
|
||||
sidebarPanelWidth: 280,
|
||||
}}
|
||||
>
|
||||
<ChromeLayout
|
||||
header={<MyHeader />}
|
||||
footer={<MyFooter />}
|
||||
navigation={<MyNav />}
|
||||
navigationPanel={<MyNavPanel />}
|
||||
sidebar={<MySidebar />}
|
||||
sidebarPanel={<MySidebarPanel />}
|
||||
banner={<MyBanner />}
|
||||
>
|
||||
<AppContent />
|
||||
</ChromeLayout>
|
||||
</ChromeLayoutConfigProvider>;
|
||||
```
|
||||
|
||||
Each slot can also be a function: `header={state => <Header expanded={state.hasSidebar} />}`
|
||||
|
||||
---
|
||||
|
||||
## CSS Variables
|
||||
|
||||
This package exposes layout dimensions and positions as global CSS variables (custom properties) for dynamic theming and consistent styling across the application. You can use these variables in your own CSS to align custom components with the layout or implement advanced theming.
|
||||
|
||||
**Available CSS variables:**
|
||||
|
||||
- **Banner**
|
||||
- `--kbn-layout--banner-top`
|
||||
- `--kbn-layout--banner-left`
|
||||
- `--kbn-layout--banner-height`
|
||||
- `--kbn-layout--banner-width`
|
||||
- **Header**
|
||||
- `--kbn-layout--header-top`
|
||||
- `--kbn-layout--header-left`
|
||||
- `--kbn-layout--header-height`
|
||||
- `--kbn-layout--header-width`
|
||||
- **Navigation**
|
||||
- `--kbn-layout--navigation-top`
|
||||
- `--kbn-layout--navigation-height`
|
||||
- `--kbn-layout--navigation-width`
|
||||
- `--kbn-layout--navigation-panel-width`
|
||||
- **Sidebar**
|
||||
- `--kbn-layout--sidebar-top`
|
||||
- `--kbn-layout--sidebar-height`
|
||||
- `--kbn-layout--sidebar-width`
|
||||
- `--kbn-layout--sidebar-panel-width`
|
||||
- **Application**
|
||||
- `--kbn-layout--application-top`
|
||||
- `--kbn-layout--application-bottom`
|
||||
- `--kbn-layout--application-left`
|
||||
- `--kbn-layout--application-right`
|
||||
- `--kbn-layout--application-height`
|
||||
- `--kbn-layout--application-width`
|
||||
- **Footer**
|
||||
- `--kbn-layout--footer-top`
|
||||
- `--kbn-layout--footer-left`
|
||||
- `--kbn-layout--footer-height`
|
||||
- `--kbn-layout--footer-width`
|
||||
|
||||
These variables are set at the `:root` level and update automatically based on the current layout state.
|
||||
|
||||
## Examples
|
||||
|
||||
See [Storybook stories](./__stories__/layout.stories.tsx) for more usage examples.
|
||||
Run with `yarn storybook sharedux` and look for "Layout" in the stories list.
|
||||
|
||||
## Types
|
||||
|
||||
See `layout.types.ts` for slot and layout state typings.
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { SerializedStyles, css } from '@emotion/react';
|
||||
import { tint, useEuiTheme } from '@elastic/eui';
|
||||
|
||||
interface BoxProps {
|
||||
color: string;
|
||||
backgroundColor: string;
|
||||
children?: ReactNode;
|
||||
label?: string;
|
||||
rootCSS?: SerializedStyles;
|
||||
labelCSS?: SerializedStyles;
|
||||
}
|
||||
|
||||
export const Box = ({ color, backgroundColor, rootCSS, label, children, labelCSS }: BoxProps) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const rootStyle = css`
|
||||
background: ${tint(backgroundColor, 0.85)};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
color: ${color};
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
gap: ${euiTheme.size.m};
|
||||
|
||||
clip-path: polygon(
|
||||
6px 2px,
|
||||
calc(100% - 6px) 2px,
|
||||
calc(100% - 2px) 6px,
|
||||
calc(100% - 2px) calc(100% - 6px),
|
||||
calc(100% - 6px) calc(100% - 2px),
|
||||
6px calc(100% - 2px),
|
||||
2px calc(100% - 6px),
|
||||
2px 6px
|
||||
);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
transparent 49%,
|
||||
${color} 49%,
|
||||
${color} 51%,
|
||||
transparent 51%
|
||||
);
|
||||
background-size: 8px 8px;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
& > * {
|
||||
z-index: 1;
|
||||
}
|
||||
${rootCSS}
|
||||
`;
|
||||
|
||||
const labelStyle = css`
|
||||
color: ${color};
|
||||
background: ${tint(backgroundColor, 0.85)};
|
||||
display: block;
|
||||
z-index: 1;
|
||||
padding: ${euiTheme.size.s} ${euiTheme.size.m};
|
||||
border-radius: ${euiTheme.border.radius.small};
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
${labelCSS}
|
||||
`;
|
||||
|
||||
return (
|
||||
<div css={rootStyle}>
|
||||
<span css={labelStyle}>{label}</span>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { Global, css } from '@emotion/react';
|
||||
import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui';
|
||||
import { ChromeLayout, ChromeLayoutConfigProvider } from '..';
|
||||
import { LayoutDebugOverlay } from '../debug/layout_debug_overlay';
|
||||
import { Box } from './box';
|
||||
|
||||
const styles = css`
|
||||
body.sb-show-main.sb-main-padded {
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
interface StoryArgs {
|
||||
debug: boolean;
|
||||
bannerHeight: number;
|
||||
footerHeight: number;
|
||||
headerHeight: number;
|
||||
navigationWidth: number;
|
||||
navigationPanelWidth: number;
|
||||
sidebarPanelWidth: number;
|
||||
sidebarWidth: number;
|
||||
includeSidebar: boolean;
|
||||
includeSidebarPanel: boolean;
|
||||
includeBanner: boolean;
|
||||
includeFooter: boolean;
|
||||
includeNavigationPanel: boolean;
|
||||
includeNavigation: boolean;
|
||||
includeHeader: boolean;
|
||||
}
|
||||
|
||||
type PropsAndArgs = React.ComponentProps<typeof ChromeLayout> & StoryArgs;
|
||||
|
||||
export default {
|
||||
title: 'Chrome/Layout',
|
||||
description: 'Chrome Layout',
|
||||
parameters: {},
|
||||
decorators: [
|
||||
(Story) => {
|
||||
return (
|
||||
<>
|
||||
<Global styles={styles} />
|
||||
<Story />
|
||||
</>
|
||||
);
|
||||
},
|
||||
],
|
||||
} as Meta<PropsAndArgs>;
|
||||
|
||||
const LayoutExample = ({
|
||||
debug,
|
||||
includeBanner,
|
||||
includeFooter,
|
||||
includeSidebar,
|
||||
includeSidebarPanel,
|
||||
includeNavigationPanel,
|
||||
includeNavigation,
|
||||
includeHeader,
|
||||
...props
|
||||
}: StoryArgs) => {
|
||||
const {
|
||||
euiTheme: { colors },
|
||||
} = useEuiTheme();
|
||||
|
||||
return (
|
||||
<>
|
||||
{debug && <LayoutDebugOverlay />}
|
||||
<ChromeLayoutConfigProvider value={{ ...props }}>
|
||||
<ChromeLayout
|
||||
banner={
|
||||
includeBanner ? (
|
||||
<Box
|
||||
color={colors.danger}
|
||||
backgroundColor={colors.textDanger}
|
||||
label="Global Banner"
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
footer={
|
||||
includeFooter ? (
|
||||
<Box
|
||||
color={colors.danger}
|
||||
backgroundColor={colors.textDanger}
|
||||
label="Global Footer"
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
header={
|
||||
includeHeader ? (
|
||||
<Box
|
||||
label="Global Header"
|
||||
color={colors.textParagraph}
|
||||
backgroundColor={colors.backgroundFilledText}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
navigation={
|
||||
includeNavigation ? (
|
||||
<Box
|
||||
label="Global Navigation"
|
||||
color={colors.textPrimary}
|
||||
backgroundColor={colors.primary}
|
||||
labelCSS={css`
|
||||
transform: translate(-50%, -50%) rotate(-90deg);
|
||||
`}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
navigationPanel={
|
||||
includeNavigationPanel ? (
|
||||
<EuiFlexGroup direction="column" gutterSize="none" css={{ height: '100%' }}>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
style={{ height: props.headerHeight }}
|
||||
css={css`
|
||||
white-space: nowrap;
|
||||
`}
|
||||
>
|
||||
<Box
|
||||
label="Nav Header"
|
||||
color={colors.textPrimary}
|
||||
backgroundColor={colors.primary}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<Box
|
||||
label="Navigation Panel"
|
||||
color={colors.textPrimary}
|
||||
backgroundColor={colors.primary}
|
||||
labelCSS={css`
|
||||
text-align: center;
|
||||
`}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : null
|
||||
}
|
||||
sidebar={
|
||||
includeSidebar ? (
|
||||
<Box
|
||||
label="Global Sidebar"
|
||||
color={colors.accentSecondary}
|
||||
backgroundColor={colors.textAccentSecondary}
|
||||
labelCSS={css`
|
||||
transform: translate(-50%, -50%) rotate(90deg);
|
||||
`}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
sidebarPanel={
|
||||
includeSidebarPanel ? (
|
||||
<EuiFlexGroup direction="column" gutterSize="none" css={{ height: '100%' }}>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
style={{ height: props.headerHeight }}
|
||||
css={css`
|
||||
white-space: nowrap;
|
||||
`}
|
||||
>
|
||||
<Box
|
||||
label="Sidebar Header"
|
||||
color={colors.accentSecondary}
|
||||
backgroundColor={colors.textAccentSecondary}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<Box
|
||||
label="Sidebar Panel"
|
||||
color={colors.accentSecondary}
|
||||
backgroundColor={colors.textAccentSecondary}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : null
|
||||
}
|
||||
>
|
||||
<Box
|
||||
label="Application"
|
||||
color={colors.textWarning}
|
||||
backgroundColor={colors.warning}
|
||||
rootCSS={css`
|
||||
height: 1000px;
|
||||
`}
|
||||
/>
|
||||
</ChromeLayout>
|
||||
</ChromeLayoutConfigProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const Layout: StoryObj<PropsAndArgs> = {
|
||||
args: {
|
||||
debug: false,
|
||||
includeBanner: true,
|
||||
includeFooter: true,
|
||||
includeNavigationPanel: true,
|
||||
includeNavigation: true,
|
||||
includeHeader: true,
|
||||
includeSidebar: true,
|
||||
includeSidebarPanel: true,
|
||||
bannerHeight: 48,
|
||||
footerHeight: 48,
|
||||
headerHeight: 48,
|
||||
navigationWidth: 48,
|
||||
navigationPanelWidth: 240,
|
||||
sidebarPanelWidth: 368,
|
||||
sidebarWidth: 48,
|
||||
},
|
||||
argTypes: {
|
||||
debug: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to include the debug overlay in the layout',
|
||||
},
|
||||
includeBanner: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to include the banner in the layout',
|
||||
},
|
||||
includeFooter: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to include the footer in the layout',
|
||||
},
|
||||
includeNavigationPanel: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to include the navigation panel in the layout',
|
||||
},
|
||||
includeSidebar: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to include the sidebar in the layout',
|
||||
},
|
||||
includeSidebarPanel: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to include the sidebar panel in the layout',
|
||||
},
|
||||
includeNavigation: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to include the navigation in the layout',
|
||||
},
|
||||
includeHeader: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to include the header in the layout',
|
||||
},
|
||||
bannerHeight: { control: 'number', description: 'Height of the banner' },
|
||||
footerHeight: { control: 'number', description: 'Height of the footer' },
|
||||
headerHeight: { control: 'number', description: 'Height of the header' },
|
||||
navigationWidth: { control: 'number', description: 'Width of the navigation' },
|
||||
navigationPanelWidth: { control: 'number', description: 'Width of the navigation panel' },
|
||||
sidebarPanelWidth: { control: 'number', description: 'Width of the sidebar panel' },
|
||||
sidebarWidth: { control: 'number', description: 'Width of the sidebar' },
|
||||
},
|
||||
render: (args) => <LayoutExample {...args} />,
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { LayoutApplication } from './layout_application';
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
import { EmotionFn } from '../types';
|
||||
|
||||
const root: EmotionFn = ({ euiTheme }) =>
|
||||
css`
|
||||
grid-area: application;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: ${euiTheme.levels.content};
|
||||
`;
|
||||
|
||||
export const styles = {
|
||||
root,
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import { useEuiOverflowScroll } from '@elastic/eui';
|
||||
|
||||
import { styles } from './layout_application.styles';
|
||||
|
||||
/**
|
||||
* The application slot wrapper
|
||||
*
|
||||
* @param props - Props for the LayoutApplication component.
|
||||
* @returns The rendered LayoutApplication component.
|
||||
*/
|
||||
export const LayoutApplication = ({ children }: { children: ReactNode }) => {
|
||||
const overflow = useEuiOverflowScroll('y');
|
||||
|
||||
return <main css={[styles.root, overflow]}>{children}</main>;
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { LayoutBanner, type LayoutBannerProps } from './layout_banner';
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
const root = css`
|
||||
grid-area: banner;
|
||||
overflow: hidden;
|
||||
position: sticky;
|
||||
width: var(--kbn-layout--banner-width);
|
||||
height: var(--kbn-layout--banner-height);
|
||||
`;
|
||||
|
||||
export const styles = {
|
||||
root,
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import { styles } from './layout_banner.styles';
|
||||
|
||||
export interface LayoutBannerProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The banner slot wrapper
|
||||
*
|
||||
* @param props - Props for the LayoutBanner component.
|
||||
* @returns The rendered LayoutBanner component.
|
||||
*/
|
||||
export const LayoutBanner = ({ children }: LayoutBannerProps) => {
|
||||
return <section css={styles.root}>{children}</section>;
|
||||
};
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
const overlayStyle = css`
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
`;
|
||||
|
||||
const rectStyle = css`
|
||||
position: absolute;
|
||||
border: 2px dashed rgba(0, 153, 255, 0.8);
|
||||
background: rgba(0, 153, 255, 0.1);
|
||||
color: #0099ff;
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
const labelStyle = css`
|
||||
background: rgba(0, 153, 255, 0.85);
|
||||
color: white;
|
||||
padding: 2px 6px;
|
||||
border-radius: 0 0 4px 0;
|
||||
font-size: 11px;
|
||||
margin: 0;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
const slots: Array<{ name: string; style: React.CSSProperties }> = [
|
||||
{
|
||||
name: 'banner',
|
||||
style: {
|
||||
top: 'var(--kbn-layout--banner-top, 0)',
|
||||
left: 'var(--kbn-layout--banner-left, 0)',
|
||||
width: 'var(--kbn-layout--banner-width, 100vw)',
|
||||
height: 'var(--kbn-layout--banner-height, 0)',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'header',
|
||||
style: {
|
||||
top: 'var(--kbn-layout--header-top, 0)',
|
||||
left: 'var(--kbn-layout--header-left, 0)',
|
||||
width: 'var(--kbn-layout--header-width, 100vw)',
|
||||
height: 'var(--kbn-layout--header-height, 0)',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'navigation',
|
||||
style: {
|
||||
top: 'var(--kbn-layout--navigation-top, 0)',
|
||||
left: '0',
|
||||
width: 'var(--kbn-layout--navigation-width, 0)',
|
||||
height: 'var(--kbn-layout--navigation-height, 0)',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'navigationPanel',
|
||||
style: {
|
||||
top: 'var(--kbn-layout--navigation-top, 0)',
|
||||
left: 'var(--kbn-layout--navigation-width, 0)',
|
||||
width: 'var(--kbn-layout--navigation-panel-width, 0)',
|
||||
height: 'var(--kbn-layout--navigation-height, 0)',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'sidebar',
|
||||
style: {
|
||||
top: 'var(--kbn-layout--sidebar-top, 0)',
|
||||
right: '0',
|
||||
width: 'var(--kbn-layout--sidebar-width, 0)',
|
||||
height: 'var(--kbn-layout--sidebar-height, 0)',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'sidebarPanel',
|
||||
style: {
|
||||
top: 'var(--kbn-layout--sidebar-top, 0)',
|
||||
right: 'var(--kbn-layout--sidebar-width, 0)',
|
||||
width: 'var(--kbn-layout--sidebar-panel-width, 0)',
|
||||
height: 'var(--kbn-layout--sidebar-height, 0)',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'application',
|
||||
style: {
|
||||
top: 'var(--kbn-layout--application-top, 0)',
|
||||
left: 'var(--kbn-layout--application-left, 0)',
|
||||
right: 'var(--kbn-layout--application-right, 0)',
|
||||
width: 'var(--kbn-layout--application-width, 0)',
|
||||
height: 'var(--kbn-layout--application-height, 0)',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'footer',
|
||||
style: {
|
||||
top: 'var(--kbn-layout--footer-top, 0)',
|
||||
left: 'var(--kbn-layout--footer-left, 0)',
|
||||
width: 'var(--kbn-layout--footer-width, 0)',
|
||||
height: 'var(--kbn-layout--footer-height, 0)',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
interface LayoutDebugOverlayProps {
|
||||
colors?: Partial<Record<string, string>>;
|
||||
}
|
||||
|
||||
const defaultColors: Record<string, string> = {
|
||||
banner: '#0099ff',
|
||||
header: '#00bfae',
|
||||
navigation: '#c837ab',
|
||||
navigationPanel: '#ff9800',
|
||||
sidebar: '#ffd600',
|
||||
sidebarPanel: '#ff3d00',
|
||||
application: '#4caf50',
|
||||
footer: '#7c4dff',
|
||||
};
|
||||
|
||||
/**
|
||||
* A debug overlay component that visually outlines the main layout slots (banner, header, navigation, sidebar, etc.)
|
||||
* using colored rectangles. This is useful for development and debugging to understand the placement and sizing of layout regions.
|
||||
*
|
||||
* @param props - {@link LayoutDebugOverlayProps} Optional colors to override the default slot colors.
|
||||
* @returns The rendered debug overlay as a fixed-position set of rectangles.
|
||||
*/
|
||||
export const LayoutDebugOverlay: React.FC<LayoutDebugOverlayProps> = ({ colors = {} }) => {
|
||||
const mergedColors = { ...defaultColors, ...colors };
|
||||
return (
|
||||
<div css={overlayStyle}>
|
||||
{slots.map((slot) => {
|
||||
const color = mergedColors[slot.name] || Object.values(defaultColors)[0];
|
||||
return (
|
||||
<div
|
||||
key={slot.name}
|
||||
css={css([
|
||||
rectStyle,
|
||||
`
|
||||
border-color: ${color};
|
||||
background: ${color}1A;
|
||||
color: ${color};
|
||||
`,
|
||||
])}
|
||||
style={slot.style}
|
||||
>
|
||||
<span css={css([labelStyle, `background: ${color};`])}>{slot.name}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { LayoutFooter, type LayoutFooterProps } from './layout_footer';
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
const root = css`
|
||||
position: sticky;
|
||||
overflow: hidden;
|
||||
grid-area: footer;
|
||||
width: var(--kbn-layout--footer-width);
|
||||
height: var(--kbn-layout--footer-height);
|
||||
`;
|
||||
|
||||
export const styles = {
|
||||
root,
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import { styles } from './layout_footer.styles';
|
||||
|
||||
export interface LayoutFooterProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The footer slot wrapper
|
||||
*
|
||||
* @param props - Props for the LayoutFooter component.
|
||||
* @returns The rendered LayoutFooter component.
|
||||
*/
|
||||
export const LayoutFooter = ({ children }: LayoutFooterProps) => {
|
||||
return <footer css={styles.root}>{children}</footer>;
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { LayoutHeader, type LayoutHeaderProps } from './layout_header';
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
const root = css`
|
||||
position: sticky;
|
||||
overflow: hidden;
|
||||
grid-area: header;
|
||||
height: var(--kbn-layout--header-height);
|
||||
`;
|
||||
|
||||
export const styles = {
|
||||
root,
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { type ReactNode } from 'react';
|
||||
import { styles } from './layout_header.styles';
|
||||
|
||||
export interface LayoutHeaderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The header slot wrapper
|
||||
*
|
||||
* @param props - Props for the LayoutHeader component.
|
||||
* @returns The rendered LayoutHeader component.
|
||||
*/
|
||||
export const LayoutHeader = ({ children }: LayoutHeaderProps) => {
|
||||
return <header css={styles.root}>{children}</header>;
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { ChromeLayout, type ChromeLayoutProps } from './layout';
|
||||
|
||||
export {
|
||||
LayoutConfigProvider as ChromeLayoutConfigProvider,
|
||||
type LayoutConfig as ChromeLayoutConfig,
|
||||
type LayoutConfigProviderProps as ChromeLayoutConfigProviderProps,
|
||||
} from './layout_config_context';
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../../../..',
|
||||
roots: ['<rootDir>/src/core/packages/chrome/layout/core-chrome-layout-components'],
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/core-chrome-layout-components",
|
||||
"owner": "@elastic/appex-sharedux",
|
||||
"group": "platform",
|
||||
"visibility": "private"
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { LayoutApplication } from './application';
|
||||
import { LayoutBanner } from './banner';
|
||||
import { LayoutNavigation, LayoutNavigationPanel } from './navigation';
|
||||
import { LayoutFooter } from './footer';
|
||||
import { LayoutHeader } from './header';
|
||||
import { LayoutSidebar, LayoutSidebarPanel } from './sidebar';
|
||||
|
||||
import { useLayoutStyles } from './layout.styles';
|
||||
import { ChromeLayoutSlots, Slot } from './layout.types';
|
||||
import { useLayoutState } from './layout_state_context';
|
||||
|
||||
export interface ChromeLayoutComponentProps extends ChromeLayoutSlots {
|
||||
// application
|
||||
children: Slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* The chrome layout component that composes slots together.
|
||||
*
|
||||
* @param props - ChromeLayoutComponentProps
|
||||
* @returns The rendered ChromeLayoutComponent.
|
||||
*/
|
||||
export const ChromeLayoutComponent = ({ children, ...props }: ChromeLayoutComponentProps) => {
|
||||
const layoutState = useLayoutState();
|
||||
const styles = useLayoutStyles(layoutState);
|
||||
|
||||
const renderSlot = (slot: Slot) => {
|
||||
if (typeof slot === 'function') {
|
||||
return slot(layoutState);
|
||||
}
|
||||
return slot;
|
||||
};
|
||||
|
||||
const banner = layoutState.hasBanner ? (
|
||||
<LayoutBanner>{renderSlot(props.banner)}</LayoutBanner>
|
||||
) : null;
|
||||
|
||||
const footer = layoutState.hasFooter ? (
|
||||
<LayoutFooter>{renderSlot(props.footer)}</LayoutFooter>
|
||||
) : null;
|
||||
|
||||
const navigationPanel = layoutState.hasNavigationPanel ? (
|
||||
<LayoutNavigationPanel width={layoutState.navigationPanelWidth}>
|
||||
{renderSlot(props.navigationPanel)}
|
||||
</LayoutNavigationPanel>
|
||||
) : null;
|
||||
|
||||
const sidebar = layoutState.hasSidebar ? (
|
||||
<LayoutSidebar>{renderSlot(props.sidebar)}</LayoutSidebar>
|
||||
) : null;
|
||||
|
||||
const sidebarPanel =
|
||||
layoutState.hasSidebar && layoutState.hasSidebarPanel ? (
|
||||
<LayoutSidebarPanel>{renderSlot(props.sidebarPanel)}</LayoutSidebarPanel>
|
||||
) : null;
|
||||
|
||||
const header = layoutState.hasHeader ? (
|
||||
<LayoutHeader>{renderSlot(props.header)}</LayoutHeader>
|
||||
) : null;
|
||||
|
||||
const navigation = layoutState.hasNavigation ? (
|
||||
<LayoutNavigation>{renderSlot(props.navigation)}</LayoutNavigation>
|
||||
) : null;
|
||||
|
||||
const application = <LayoutApplication>{renderSlot(children)}</LayoutApplication>;
|
||||
|
||||
return (
|
||||
<div css={styles.css} style={styles.style}>
|
||||
{banner}
|
||||
{header}
|
||||
{navigation}
|
||||
{navigationPanel}
|
||||
{application}
|
||||
{footer}
|
||||
{sidebar}
|
||||
{sidebarPanel}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
import { LayoutState } from './layout.types';
|
||||
|
||||
const cssProp = css`
|
||||
align-items: baseline;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
|
||||
display: grid;
|
||||
|
||||
grid-template-areas:
|
||||
'banner banner banner banner banner'
|
||||
'navigation navigationPanel header sidebarPanel sidebar'
|
||||
'navigation navigationPanel application sidebarPanel sidebar'
|
||||
'navigation navigationPanel footer sidebarPanel sidebar';
|
||||
`;
|
||||
|
||||
// TODO: clintandrewhall - Handle smaller screens using `useEuiBreakpoints`.
|
||||
export const useLayoutStyles = (layoutState: LayoutState) => {
|
||||
const {
|
||||
navigationPanelWidth,
|
||||
navigationWidth,
|
||||
sidebarPanelWidth,
|
||||
sidebarWidth,
|
||||
bannerHeight,
|
||||
headerHeight,
|
||||
footerHeight,
|
||||
} = layoutState;
|
||||
|
||||
const style = {
|
||||
gridTemplateColumns: `
|
||||
${navigationWidth}px
|
||||
${navigationPanelWidth}px
|
||||
1fr
|
||||
${sidebarPanelWidth}px
|
||||
${sidebarWidth}px
|
||||
`,
|
||||
gridTemplateRows: `
|
||||
${bannerHeight}px
|
||||
${headerHeight}px
|
||||
1fr
|
||||
${footerHeight}px
|
||||
`,
|
||||
};
|
||||
|
||||
return {
|
||||
css: cssProp,
|
||||
style,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { ChromeLayoutComponent, type ChromeLayoutComponentProps } from './layout.component';
|
||||
import { LayoutGlobalCSS } from './layout_global_css';
|
||||
import { LayoutStateProvider } from './layout_state_context';
|
||||
|
||||
/**
|
||||
* Props for the ChromeLayout component.
|
||||
* @public
|
||||
*/
|
||||
export type ChromeLayoutProps = ChromeLayoutComponentProps;
|
||||
|
||||
/**
|
||||
* The main Chrome layout component.
|
||||
* Sets up the layout and required global css.
|
||||
*
|
||||
* @public
|
||||
* @param props - Props for the ChromeLayout component.
|
||||
* @returns The rendered ChromeLayout component.
|
||||
*/
|
||||
export const ChromeLayout = ({ children, ...props }: ChromeLayoutProps) => {
|
||||
return (
|
||||
<LayoutStateProvider {...props}>
|
||||
<LayoutGlobalCSS />
|
||||
<ChromeLayoutComponent {...props}>{children}</ChromeLayoutComponent>
|
||||
</LayoutStateProvider>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Dimensions for each layout section in the Chrome UI.
|
||||
*
|
||||
* This interface defines the pixel sizes for key layout areas such as banner, footer, header,
|
||||
* navigation, and sidebar, including their respective panel widths.
|
||||
*/
|
||||
export interface LayoutDimensions {
|
||||
bannerHeight: number;
|
||||
footerHeight: number;
|
||||
headerHeight: number;
|
||||
navigationWidth: number;
|
||||
navigationPanelWidth: number;
|
||||
sidebarWidth: number;
|
||||
sidebarPanelWidth: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The state of the layout.
|
||||
*/
|
||||
export interface LayoutState extends LayoutDimensions {
|
||||
hasBanner: boolean;
|
||||
hasFooter: boolean;
|
||||
hasSidebar: boolean;
|
||||
hasSidebarPanel: boolean;
|
||||
hasHeader: boolean;
|
||||
hasNavigation: boolean;
|
||||
hasNavigationPanel: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for the slots.
|
||||
*/
|
||||
export type SlotProps = LayoutState;
|
||||
|
||||
/**
|
||||
* A slot is a React node or a function that returns a React node.
|
||||
*/
|
||||
export type Slot = React.ReactNode | ((props: SlotProps) => React.ReactNode);
|
||||
|
||||
/**
|
||||
* Supported slots for the layout
|
||||
*/
|
||||
export interface ChromeLayoutSlots {
|
||||
header?: Slot | null;
|
||||
navigation?: Slot | null;
|
||||
navigationPanel?: Slot | null;
|
||||
banner?: Slot | null;
|
||||
footer?: Slot | null;
|
||||
sidebarPanel?: Slot | null;
|
||||
sidebar?: Slot | null;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, ReactNode } from 'react';
|
||||
import { LayoutDimensions } from './layout.types';
|
||||
|
||||
/**
|
||||
* Configuration for the layout.
|
||||
* @public
|
||||
*/
|
||||
export type LayoutConfig = Pick<
|
||||
Partial<LayoutDimensions>,
|
||||
| 'bannerHeight'
|
||||
| 'headerHeight'
|
||||
| 'footerHeight'
|
||||
| 'navigationWidth'
|
||||
| 'navigationPanelWidth'
|
||||
| 'sidebarWidth'
|
||||
| 'sidebarPanelWidth'
|
||||
>;
|
||||
|
||||
const LayoutConfigContext = createContext<LayoutConfig | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Props for the LayoutConfigProvider component.
|
||||
* @public
|
||||
*/
|
||||
export interface LayoutConfigProviderProps {
|
||||
value: LayoutConfig;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider of the layout config
|
||||
* @public
|
||||
*/
|
||||
export const LayoutConfigProvider = ({ value, children }: LayoutConfigProviderProps) => {
|
||||
return <LayoutConfigContext.Provider value={value}>{children}</LayoutConfigContext.Provider>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to access the layout configuration.
|
||||
* @internal
|
||||
* @returns The current layout configuration
|
||||
* @throws Error if used outside of a LayoutConfigProvider
|
||||
*/
|
||||
export function useLayoutConfig(): LayoutConfig {
|
||||
const context = useContext(LayoutConfigContext);
|
||||
if (!context) {
|
||||
throw new Error('useLayoutConfig must be used within a LayoutConfigProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Global, css } from '@emotion/react';
|
||||
import { useLayoutState } from './layout_state_context';
|
||||
|
||||
/**
|
||||
* Sets up global CSS for the layout using the CSS variables (custom properties) approach.
|
||||
* This enables dynamic theming and consistent styling across the application by defining reusable variables at the global scope.
|
||||
*
|
||||
* @remarks
|
||||
* The following CSS variables are defined:
|
||||
*
|
||||
* Banner:
|
||||
* --kbn-layout--banner-top
|
||||
* --kbn-layout--banner-left
|
||||
* --kbn-layout--banner-height
|
||||
* --kbn-layout--banner-width
|
||||
*
|
||||
* Header:
|
||||
* --kbn-layout--header-top
|
||||
* --kbn-layout--header-left
|
||||
* --kbn-layout--header-height
|
||||
* --kbn-layout--header-width
|
||||
*
|
||||
* Navigation:
|
||||
* --kbn-layout--navigation-top
|
||||
* --kbn-layout--navigation-height
|
||||
* --kbn-layout--navigation-width
|
||||
* --kbn-layout--navigation-panel-width
|
||||
*
|
||||
* Sidebar:
|
||||
* --kbn-layout--sidebar-top
|
||||
* --kbn-layout--sidebar-height
|
||||
* --kbn-layout--sidebar-width
|
||||
* --kbn-layout--sidebar-panel-width
|
||||
*
|
||||
* Application:
|
||||
* --kbn-layout--application-top
|
||||
* --kbn-layout--application-bottom
|
||||
* --kbn-layout--application-left
|
||||
* --kbn-layout--application-right
|
||||
* --kbn-layout--application-height
|
||||
* --kbn-layout--application-width
|
||||
*
|
||||
* Footer:
|
||||
* --kbn-layout--footer-top
|
||||
* --kbn-layout--footer-left
|
||||
* --kbn-layout--footer-height
|
||||
* --kbn-layout--footer-width
|
||||
*
|
||||
* These variables are available globally for consistent layout styling and dynamic updates.
|
||||
* @returns The rendered GlobalCSS component.
|
||||
*/
|
||||
export const LayoutGlobalCSS = () => {
|
||||
const {
|
||||
bannerHeight,
|
||||
footerHeight,
|
||||
headerHeight,
|
||||
navigationWidth,
|
||||
navigationPanelWidth,
|
||||
sidebarWidth,
|
||||
sidebarPanelWidth,
|
||||
} = useLayoutState();
|
||||
|
||||
const banner = css`
|
||||
--kbn-layout--banner-top: 0;
|
||||
--kbn-layout--banner-left: 0;
|
||||
--kbn-layout--banner-height: ${bannerHeight}px;
|
||||
--kbn-layout--banner-width: 100vw;
|
||||
`;
|
||||
|
||||
const header = css`
|
||||
--kbn-layout--header-top: var(--kbn-layout--banner-height);
|
||||
--kbn-layout--header-left: ${navigationWidth + navigationPanelWidth}px;
|
||||
--kbn-layout--header-height: ${headerHeight}px;
|
||||
--kbn-layout--header-width: calc(
|
||||
100vw - var(--kbn-layout--sidebar-width) - var(--kbn-layout--sidebar-panel-width) -
|
||||
var(--kbn-layout--header-left)
|
||||
);
|
||||
`;
|
||||
|
||||
const navigation = css`
|
||||
--kbn-layout--navigation-top: ${bannerHeight}px;
|
||||
--kbn-layout--navigation-height: calc(100vh - var(--kbn-layout--navigation-top));
|
||||
--kbn-layout--navigation-width: ${navigationWidth}px;
|
||||
--kbn-layout--navigation-panel-width: ${navigationPanelWidth}px;
|
||||
`;
|
||||
|
||||
const sidebar = css`
|
||||
--kbn-layout--sidebar-top: ${bannerHeight}px;
|
||||
--kbn-layout--sidebar-height: calc(100vh - var(--kbn-layout--sidebar-top));
|
||||
--kbn-layout--sidebar-width: ${sidebarWidth}px;
|
||||
--kbn-layout--sidebar-panel-width: ${sidebarPanelWidth}px;
|
||||
`;
|
||||
|
||||
const application = css`
|
||||
--kbn-layout--application-top: ${headerHeight + bannerHeight}px;
|
||||
--kbn-layout--application-bottom: ${footerHeight}px;
|
||||
--kbn-layout--application-left: ${navigationWidth + navigationPanelWidth}px;
|
||||
--kbn-layout--application-right: ${sidebarWidth + sidebarPanelWidth}px;
|
||||
--kbn-layout--application-height: calc(
|
||||
100vh - var(--kbn-layout--application-top) - var(--kbn-layout--application-bottom)
|
||||
);
|
||||
--kbn-layout--application-width: calc(
|
||||
100vw - var(--kbn-layout--navigation-width) - var(--kbn-layout--navigation-panel-width) -
|
||||
var(--kbn-layout--sidebar-width) - var(--kbn-layout--sidebar-panel-width)
|
||||
);
|
||||
`;
|
||||
|
||||
const footer = css`
|
||||
--kbn-layout--footer-top: calc(100vh - var(--kbn-layout--footer-height));
|
||||
--kbn-layout--footer-left: ${navigationWidth + navigationPanelWidth}px;
|
||||
--kbn-layout--footer-height: ${footerHeight}px;
|
||||
--kbn-layout--footer-width: var(--kbn-layout--application-width);
|
||||
`;
|
||||
|
||||
const styles = css`
|
||||
:root {
|
||||
${banner}
|
||||
${header}
|
||||
${navigation}
|
||||
${sidebar}
|
||||
${application}
|
||||
${footer}
|
||||
}
|
||||
`;
|
||||
|
||||
return <Global styles={styles} />;
|
||||
};
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { createContext, ReactNode, useContext } from 'react';
|
||||
import { ChromeLayoutSlots, LayoutState } from './layout.types';
|
||||
import { useLayoutConfig } from './layout_config_context';
|
||||
|
||||
export interface LayoutStateProps extends ChromeLayoutSlots {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const LayoutStateContext = createContext<LayoutState | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* The layout state provider component.
|
||||
* Wires up the LayoutConfig to the layout state.
|
||||
*
|
||||
* @param props - Props for the LayoutStateProvider component.
|
||||
* @returns The rendered LayoutStateProvider component.
|
||||
*/
|
||||
export const LayoutStateProvider = ({ children, ...props }: LayoutStateProps) => {
|
||||
// Get layout config from context
|
||||
const layoutConfig = useLayoutConfig();
|
||||
const slots = {
|
||||
Header: props.header || null,
|
||||
Navigation: props.navigation || null,
|
||||
NavigationPanel: props.navigationPanel || null,
|
||||
Banner: props.banner || null,
|
||||
Footer: props.footer || null,
|
||||
SidebarPanel: props.sidebarPanel || null,
|
||||
Sidebar: props.sidebar || null,
|
||||
};
|
||||
|
||||
const hasBanner = !!slots.Banner;
|
||||
const hasFooter = !!slots.Footer;
|
||||
const hasSidebar = !!slots.Sidebar;
|
||||
const hasSidebarPanel = !!slots.SidebarPanel;
|
||||
const hasNavigationPanel = !!slots.NavigationPanel;
|
||||
const hasHeader = !!slots.Header;
|
||||
const hasNavigation = !!slots.Navigation;
|
||||
|
||||
const layoutState: LayoutState = {
|
||||
hasBanner,
|
||||
bannerHeight: hasBanner ? layoutConfig.bannerHeight ?? 0 : 0,
|
||||
hasFooter,
|
||||
footerHeight: hasFooter ? layoutConfig.footerHeight ?? 0 : 0,
|
||||
hasHeader,
|
||||
headerHeight: hasHeader ? layoutConfig.headerHeight ?? 0 : 0,
|
||||
hasNavigation,
|
||||
navigationWidth: hasNavigation ? layoutConfig.navigationWidth ?? 0 : 0,
|
||||
hasNavigationPanel,
|
||||
navigationPanelWidth: hasNavigationPanel ? layoutConfig.navigationPanelWidth ?? 0 : 0,
|
||||
hasSidebar,
|
||||
sidebarWidth: hasSidebar ? layoutConfig.sidebarWidth ?? 0 : 0,
|
||||
hasSidebarPanel,
|
||||
sidebarPanelWidth: hasSidebar && hasSidebarPanel ? layoutConfig.sidebarPanelWidth ?? 0 : 0,
|
||||
};
|
||||
|
||||
return <LayoutStateContext.Provider value={layoutState}>{children}</LayoutStateContext.Provider>;
|
||||
};
|
||||
|
||||
export const useLayoutState = () => {
|
||||
const context = useContext(LayoutStateContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useLayoutState must be used within a LayoutStateProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
|
@ -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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { LayoutNavigation, type LayoutNavigationProps } from './layout_navigation';
|
||||
export { LayoutNavigationPanel, type LayoutNavigationPanelProps } from './layout_navigation_panel';
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import { EmotionFn } from '../types';
|
||||
|
||||
const root: EmotionFn = ({ euiTheme }) => css`
|
||||
position: sticky;
|
||||
z-index: ${euiTheme.levels.navigation};
|
||||
grid-area: navigation;
|
||||
align-self: start;
|
||||
height: 100%;
|
||||
width: var(--kbn-layout--navigation-width);
|
||||
`;
|
||||
|
||||
export const styles = {
|
||||
root,
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { type ReactNode } from 'react';
|
||||
import { styles } from './layout_navigation.styles';
|
||||
|
||||
export interface LayoutNavigationProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The navigation slot wrapper
|
||||
*
|
||||
* @param props - Props for the LayoutNavigation component.
|
||||
* @returns The rendered LayoutNavigation component.
|
||||
*/
|
||||
export const LayoutNavigation = ({ children }: LayoutNavigationProps) => {
|
||||
return <nav css={styles.root}>{children}</nav>;
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import { EmotionFn } from '../types';
|
||||
|
||||
const root: EmotionFn = ({ euiTheme }) => css`
|
||||
position: sticky;
|
||||
z-index: ${euiTheme.levels.navigation};
|
||||
grid-area: navigationPanel;
|
||||
align-self: start;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
export const styles = {
|
||||
root,
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { type ReactNode } from 'react';
|
||||
import { styles } from './layout_navigation_panel.styles';
|
||||
|
||||
export interface LayoutNavigationPanelProps {
|
||||
children: ReactNode;
|
||||
width: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The navigation panel slot wrapper
|
||||
*
|
||||
* @param props - Props for the LayoutNavigationPanel component.
|
||||
* @returns The rendered LayoutNavigationPanel component.
|
||||
*/
|
||||
export const LayoutNavigationPanel = ({ children, width }: LayoutNavigationPanelProps) => {
|
||||
return (
|
||||
<nav css={styles.root} style={{ width }}>
|
||||
{children}
|
||||
</nav>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/core-chrome-layout-components",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0"
|
||||
}
|
|
@ -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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { LayoutSidebar, type LayoutSidebarProps } from './layout_sidebar';
|
||||
export { LayoutSidebarPanel, type LayoutSidebarPanelProps } from './layout_sidebar_panel';
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
const root = css`
|
||||
position: sticky;
|
||||
overflow: hidden;
|
||||
grid-area: sidebar;
|
||||
align-self: start;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: var(--kbn-layout--sidebar-width);
|
||||
`;
|
||||
|
||||
export const styles = {
|
||||
root,
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import { styles } from './layout_sidebar.styles';
|
||||
|
||||
export interface LayoutSidebarProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sidebar slot wrapper
|
||||
*
|
||||
* @param props - Props for the LayoutSidebar component.
|
||||
* @returns The rendered LayoutSidebar component.
|
||||
*/
|
||||
export const LayoutSidebar = ({ children }: LayoutSidebarProps) => {
|
||||
return <nav css={styles.root}>{children}</nav>;
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
const root = css`
|
||||
align-self: start;
|
||||
display: flex;
|
||||
grid-area: sidebarPanel;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: sticky;
|
||||
width: var(--kbn-layout--sidebar-panel-width);
|
||||
`;
|
||||
|
||||
export const styles = {
|
||||
root,
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { styles } from './layout_sidebar_panel.styles';
|
||||
|
||||
export interface LayoutSidebarPanelProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sidebar panel slot wrapper
|
||||
*
|
||||
* @param props - Props for the LayoutSidebarPanel component.
|
||||
* @returns The rendered LayoutSidebarPanel component.
|
||||
*/
|
||||
export const LayoutSidebarPanel = ({ children }: LayoutSidebarPanelProps) => {
|
||||
return <aside css={styles.root}>{children}</aside>;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"extends": "../../../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react",
|
||||
"@kbn/ambient-ui-types",
|
||||
"@kbn/ambient-storybook-types",
|
||||
"@emotion/react/types/css-prop",
|
||||
"../../../../../../typings/emotion.d.ts"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": []
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { EuiThemeColorMode, EuiThemeComputed } from '@elastic/eui';
|
||||
import { SerializedStyles } from '@emotion/serialize';
|
||||
|
||||
export type EmotionFn = ({
|
||||
euiTheme,
|
||||
colorMode,
|
||||
}: {
|
||||
euiTheme: EuiThemeComputed;
|
||||
colorMode: EuiThemeColorMode;
|
||||
}) => SerializedStyles;
|
|
@ -14,5 +14,6 @@ module.exports = {
|
|||
stories: [
|
||||
'../../**/*.stories.+(tsx|mdx)',
|
||||
'../../../../shared/shared-ux/**/*.stories.+(tsx|mdx)',
|
||||
'../../../../../../core/packages/chrome/**/*.stories.+(tsx|mdx)',
|
||||
],
|
||||
};
|
||||
|
|
|
@ -304,6 +304,8 @@
|
|||
"@kbn/core-chrome-browser-internal/*": ["src/core/packages/chrome/browser-internal/*"],
|
||||
"@kbn/core-chrome-browser-mocks": ["src/core/packages/chrome/browser-mocks"],
|
||||
"@kbn/core-chrome-browser-mocks/*": ["src/core/packages/chrome/browser-mocks/*"],
|
||||
"@kbn/core-chrome-layout-components": ["src/core/packages/chrome/layout/core-chrome-layout-components"],
|
||||
"@kbn/core-chrome-layout-components/*": ["src/core/packages/chrome/layout/core-chrome-layout-components/*"],
|
||||
"@kbn/core-config-server-internal": ["src/core/packages/config/server-internal"],
|
||||
"@kbn/core-config-server-internal/*": ["src/core/packages/config/server-internal/*"],
|
||||
"@kbn/core-custom-branding-browser": ["src/core/packages/custom-branding/browser"],
|
||||
|
|
|
@ -4375,6 +4375,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/core-chrome-layout-components@link:src/core/packages/chrome/layout/core-chrome-layout-components":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/core-config-server-internal@link:src/core/packages/config/server-internal":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue