1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Menu: extended kinds of positions when rendered inside parent's element (usePortal=false)

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2021-04-06 15:03:21 +03:00
parent 33c405bdcf
commit 4e459fdff6
4 changed files with 58 additions and 21 deletions

View File

@ -32,7 +32,6 @@
margin-bottom: $margin; margin-bottom: $margin;
.Icon { .Icon {
margin-bottom: $margin * 1.5;
border-radius: $radius; border-radius: $radius;
padding: $padding / 3; padding: $padding / 3;
color: var(--textColorPrimary); color: var(--textColorPrimary);

View File

@ -118,13 +118,13 @@ export class ClustersMenu extends React.Component<Props> {
<div className="WorkspaceMenu"> <div className="WorkspaceMenu">
<Icon big material="menu" id="workspace-menu-icon" data-test-id="workspace-menu" /> <Icon big material="menu" id="workspace-menu-icon" data-test-id="workspace-menu" />
<Menu <Menu
usePortal
htmlFor="workspace-menu-icon" htmlFor="workspace-menu-icon"
className="WorkspaceMenu" className="WorkspaceMenu"
isOpen={this.workspaceMenuVisible} isOpen={this.workspaceMenuVisible}
open={() => this.workspaceMenuVisible = true} open={() => this.workspaceMenuVisible = true}
close={() => this.workspaceMenuVisible = false} close={() => this.workspaceMenuVisible = false}
toggleEvent="click" toggleEvent="click"
position={{right: "outside", top: "inside"}}
> >
<MenuItem onClick={() => navigate(addClusterURL())} data-test-id="add-cluster-menu-item"> <MenuItem onClick={() => navigate(addClusterURL())} data-test-id="add-cluster-menu-item">
<Icon small material="add" /> Add Cluster <Icon small material="add" /> Add Cluster

View File

@ -9,6 +9,8 @@
border: 1px solid $borderColor; border: 1px solid $borderColor;
z-index: 101; z-index: 101;
// TODO: support predefined positions setup when rendered in document.body
// auto-positioning currently applied inside component on menu open
&.portal { &.portal {
left: -1000px; left: -1000px;
top: -1000px; top: -1000px;
@ -22,25 +24,48 @@
} }
} }
// predefined positions without `usePortal` (default) where menu is rendered inside it's parent dom tree.
&:not(.portal) { &:not(.portal) {
margin: $margin 0;
&.left { &.left {
&-inside {
left: 0; left: 0;
} }
&-outside {
right: 100%;
}
}
&.right { &.right {
&-inside {
right: 0; right: 0;
} }
&-outside {
left: 100%;
}
}
&.top { &.top {
&-inside {
top: 0;
}
&-outside {
bottom: 100%; bottom: 100%;
} }
}
&.bottom { &.bottom {
&-inside {
bottom: 0;
}
&-outside {
top: 100%; top: 100%;
} }
} }
}
&:empty { &:empty {
display: none; display: none;

View File

@ -9,12 +9,13 @@ import debounce from "lodash/debounce";
export const MenuContext = React.createContext<MenuContextValue>(null); export const MenuContext = React.createContext<MenuContextValue>(null);
export type MenuContextValue = Menu; export type MenuContextValue = Menu;
export type MenuPositionSide = "inside" | "outside";
export interface MenuPosition { export interface MenuPosition {
left?: boolean; left?: MenuPositionSide;
top?: boolean; top?: MenuPositionSide;
right?: boolean; right?: MenuPositionSide;
bottom?: boolean; bottom?: MenuPositionSide;
} }
export interface MenuProps { export interface MenuProps {
@ -35,11 +36,16 @@ export interface MenuProps {
} }
interface State { interface State {
position?: MenuPosition; position?: {
left?: boolean;
top?: boolean;
right?: boolean;
bottom?: boolean;
};
} }
const defaultPropsMenu: Partial<MenuProps> = { const defaultPropsMenu: Partial<MenuProps> = {
position: { right: true, bottom: true }, position: { left: "inside", bottom: "outside" },
autoFocus: false, autoFocus: false,
usePortal: false, usePortal: false,
closeOnClickItem: true, closeOnClickItem: true,
@ -137,7 +143,12 @@ export class Menu extends React.Component<MenuProps, State> {
} }
// setup initial position // setup initial position
const position: MenuPosition = { left: true, bottom: true }; const position = {
bottom: true,
left: true,
right: false,
top: false,
};
this.elem.style.left = `${left}px`; this.elem.style.left = `${left}px`;
this.elem.style.top = `${bottom}px`; this.elem.style.top = `${bottom}px`;
@ -248,12 +259,14 @@ export class Menu extends React.Component<MenuProps, State> {
} }
render() { render() {
const { position, id } = this.props; const { position = {}, id, className } = this.props;
let { className, usePortal } = this.props; let { usePortal } = this.props;
className = cssNames("Menu", className, this.state.position || position, { const positionClasses = usePortal
portal: usePortal, ? this.state.position // auto-corrected position to be visible in viewport
}); : Object.entries(position).map(entry => entry.join("-")); // positioning relative to parent
const classNames = cssNames("Menu", className, { portal: usePortal }, positionClasses);
let children = this.props.children as ReactElement<any>; let children = this.props.children as ReactElement<any>;
@ -273,7 +286,7 @@ export class Menu extends React.Component<MenuProps, State> {
const menu = ( const menu = (
<MenuContext.Provider value={this}> <MenuContext.Provider value={this}>
<Animate enter={this.isOpen}> <Animate enter={this.isOpen}>
<ul id={id} className={className} ref={this.bindRef}> <ul id={id} className={classNames} ref={this.bindRef}>
{menuItems} {menuItems}
</ul> </ul>
</Animate> </Animate>