mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
cleanup Lens repo with tighter linting
Signed-off-by: Sebastian Malton <smalton@mirantis.com>
This commit is contained in:
1 parent
e468105143
commit
b1ff34879a
470 files changed
+8447
-7443
No files matched your search
@@ -1,2 +1,2 @@
|
||||
export * from './menu'
|
||||
export * from './menu-picker'
|
||||
export * from './menu';
|
||||
export * from './menu-picker';
|
||||
@@ -1,4 +1,4 @@
|
||||
import "./menu-actions.scss"
|
||||
import "./menu-actions.scss";
|
||||
|
||||
import React, { isValidElement } from "react";
|
||||
import { observable } from "mobx";
|
||||
@@ -25,7 +25,7 @@ export interface MenuActionsProps extends Partial<MenuProps> {
|
||||
export class MenuActions extends React.Component<MenuActionsProps> {
|
||||
static defaultProps: MenuActionsProps = {
|
||||
get removeConfirmationMessage() {
|
||||
return _i18n._(t`Remove item?`)
|
||||
return _i18n._(t`Remove item?`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,15 @@ export class MenuActions extends React.Component<MenuActionsProps> {
|
||||
|
||||
@observable isOpen = !!this.props.toolbar;
|
||||
|
||||
toggle = () => {
|
||||
if (this.props.toolbar) return;
|
||||
toggle = (): void => {
|
||||
if (this.props.toolbar) {
|
||||
return;
|
||||
}
|
||||
this.isOpen = !this.isOpen;
|
||||
};
|
||||
|
||||
@autobind()
|
||||
remove() {
|
||||
remove(): void {
|
||||
const { removeAction } = this.props;
|
||||
let { removeConfirmationMessage } = this.props;
|
||||
if (typeof removeConfirmationMessage === "function") {
|
||||
@@ -49,11 +51,13 @@ export class MenuActions extends React.Component<MenuActionsProps> {
|
||||
ok: removeAction,
|
||||
labelOk: _i18n._(t`Remove`),
|
||||
message: <div>{removeConfirmationMessage}</div>,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
renderTriggerIcon() {
|
||||
if (this.props.toolbar) return;
|
||||
renderTriggerIcon(): JSX.Element {
|
||||
if (this.props.toolbar) {
|
||||
return;
|
||||
}
|
||||
const { triggerIcon = "more_vert" } = this.props;
|
||||
let className: string;
|
||||
if (isValidElement<HTMLElement>(triggerIcon)) {
|
||||
@@ -66,18 +70,19 @@ export class MenuActions extends React.Component<MenuActionsProps> {
|
||||
material: isString(triggerIcon) ? triggerIcon : undefined,
|
||||
active: this.isOpen,
|
||||
...(typeof triggerIcon === "object" ? triggerIcon : {}),
|
||||
}
|
||||
};
|
||||
if (iconProps.tooltip && this.isOpen) {
|
||||
delete iconProps.tooltip; // don't show tooltip for icon when menu is open
|
||||
}
|
||||
return (
|
||||
<Icon {...iconProps}/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
const {
|
||||
className, toolbar, children, updateAction, removeAction, triggerIcon, removeConfirmationMessage,
|
||||
className, toolbar, children, updateAction, removeAction,
|
||||
triggerIcon: _triggerIcon, removeConfirmationMessage: _removeConfirmationMessage,
|
||||
...menuProps
|
||||
} = this.props;
|
||||
const menuClassName = cssNames("MenuActions flex", className, {
|
||||
@@ -113,6 +118,6 @@ export class MenuActions extends React.Component<MenuActionsProps> {
|
||||
)}
|
||||
</Menu>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import './menu-picker.scss'
|
||||
import './menu-picker.scss';
|
||||
|
||||
import React, { useRef, useState } from "react";
|
||||
import { cssNames } from "../../utils";
|
||||
@@ -12,12 +12,12 @@ interface Props extends Partial<MenuProps> {
|
||||
waiting?: boolean;
|
||||
}
|
||||
|
||||
export function MenuPicker(props: Props) {
|
||||
export function MenuPicker(props: Props): JSX.Element {
|
||||
const id = useRef(uniqueId("menu_picker_")).current;
|
||||
const { className, title, waiting, children, ...menuProps } = props;
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
|
||||
const toggle = () => setOpen(!isOpen);
|
||||
const toggle = (): void => setOpen(!isOpen);
|
||||
|
||||
return (
|
||||
<div className={cssNames("MenuPicker", className, { waiting })}>
|
||||
@@ -38,5 +38,5 @@ export function MenuPicker(props: Props) {
|
||||
{children}
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,86 @@
|
||||
import './menu.scss'
|
||||
import './menu.scss';
|
||||
|
||||
import React, { Fragment, ReactElement, ReactNode } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { autobind, cssNames, noop } from "../../utils";
|
||||
import { Animate } from "../animate";
|
||||
import { Icon, IconProps } from "../icon";
|
||||
import debounce from "lodash/debounce"
|
||||
import debounce from "lodash/debounce";
|
||||
|
||||
export const MenuContext = React.createContext<MenuContextValue>(null);
|
||||
export type MenuContextValue = Menu;
|
||||
|
||||
export interface MenuItemProps extends React.HTMLProps<any> {
|
||||
icon?: string | Partial<IconProps>;
|
||||
disabled?: boolean;
|
||||
active?: boolean;
|
||||
spacer?: boolean;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
const defaultPropsMenuItem: Partial<MenuItemProps> = {
|
||||
onClick: noop,
|
||||
};
|
||||
|
||||
@autobind()
|
||||
export class MenuItem extends React.Component<MenuItemProps> {
|
||||
static defaultProps = defaultPropsMenuItem as object;
|
||||
static contextType = MenuContext;
|
||||
|
||||
public context: MenuContextValue;
|
||||
public elem: HTMLElement;
|
||||
|
||||
get isFocusable(): boolean {
|
||||
const { disabled, spacer } = this.props;
|
||||
return !(disabled || spacer);
|
||||
}
|
||||
|
||||
get isLink(): boolean {
|
||||
return !!this.props.href;
|
||||
}
|
||||
|
||||
onClick(evt: React.MouseEvent): void {
|
||||
const menu = this.context;
|
||||
const { spacer, onClick } = this.props;
|
||||
if (spacer) {
|
||||
return;
|
||||
}
|
||||
onClick(evt);
|
||||
if (menu.props.closeOnClickItem && !evt.defaultPrevented) {
|
||||
menu.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected bindRef(elem: HTMLElement): void {
|
||||
this.elem = elem;
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const { className, disabled, active, spacer, icon, children, ...props } = this.props;
|
||||
let iconProps: Partial<IconProps>;
|
||||
if (icon) {
|
||||
iconProps = {};
|
||||
if (typeof icon === "string") {
|
||||
iconProps.material = icon;
|
||||
} else {
|
||||
Object.assign(iconProps, icon);
|
||||
}
|
||||
}
|
||||
const elemProps: React.HTMLProps<any> = {
|
||||
tabIndex: this.isFocusable ? 0 : -1,
|
||||
...props,
|
||||
className: cssNames("MenuItem", className, { disabled, active, spacer }),
|
||||
onClick: this.onClick,
|
||||
children: icon ? <><Icon {...iconProps} /> {children}</> : children,
|
||||
ref: this.bindRef,
|
||||
};
|
||||
if (this.isLink) {
|
||||
return <a {...elemProps} />;
|
||||
}
|
||||
return <li {...elemProps} />;
|
||||
}
|
||||
}
|
||||
|
||||
interface MenuPosition {
|
||||
left?: boolean;
|
||||
top?: boolean;
|
||||
@@ -55,17 +126,18 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
|
||||
public state: State = {};
|
||||
|
||||
get isOpen() {
|
||||
get isOpen(): boolean {
|
||||
return !!this.props.isOpen;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount(): void {
|
||||
if (!this.props.usePortal) {
|
||||
const parent = this.elem.parentElement;
|
||||
const position = window.getComputedStyle(parent).position;
|
||||
if (position === 'static') parent.style.position = 'relative';
|
||||
}
|
||||
else if (this.isOpen) {
|
||||
if (position === 'static') {
|
||||
parent.style.position = 'relative';
|
||||
}
|
||||
} else if (this.isOpen) {
|
||||
this.refreshPosition();
|
||||
}
|
||||
this.opener = document.getElementById(this.props.htmlFor); // might not exist in sub-menus
|
||||
@@ -79,7 +151,7 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
window.addEventListener('scroll', this.onScrollOutside, true);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
componentWillUnmount(): void {
|
||||
if (this.opener) {
|
||||
this.opener.removeEventListener('click', this.toggle);
|
||||
this.opener.removeEventListener('keydown', this.onKeyDown);
|
||||
@@ -90,15 +162,15 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
window.removeEventListener('scroll', this.onScrollOutside, true);
|
||||
}
|
||||
|
||||
protected get focusableItems() {
|
||||
protected get focusableItems(): MenuItem[] {
|
||||
return Object.values(this.items).filter(item => item.isFocusable);
|
||||
}
|
||||
|
||||
protected get focusedItem() {
|
||||
protected get focusedItem(): MenuItem {
|
||||
return this.focusableItems.find(item => item.elem === document.activeElement);
|
||||
}
|
||||
|
||||
protected focusNextItem(reverse = false) {
|
||||
protected focusNextItem(reverse = false): void {
|
||||
const items = this.focusableItems;
|
||||
const activeIndex = items.findIndex(item => item === this.focusedItem);
|
||||
if (!items.length) {
|
||||
@@ -106,16 +178,19 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
}
|
||||
if (activeIndex > -1) {
|
||||
let nextItem = reverse ? items[activeIndex - 1] : items[activeIndex + 1];
|
||||
if (!nextItem) nextItem = items[activeIndex];
|
||||
if (!nextItem) {
|
||||
nextItem = items[activeIndex];
|
||||
}
|
||||
nextItem.elem.focus();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
items[0].elem.focus();
|
||||
}
|
||||
}
|
||||
|
||||
refreshPosition = debounce(() => {
|
||||
if (!this.props.usePortal || !this.opener) return;
|
||||
if (!this.props.usePortal || !this.opener) {
|
||||
return;
|
||||
}
|
||||
const { width, height } = this.opener.getBoundingClientRect();
|
||||
let { left, top, bottom, right } = this.opener.getBoundingClientRect();
|
||||
const withScroll = window.getComputedStyle(this.elem).position !== "fixed";
|
||||
@@ -130,8 +205,8 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
|
||||
// setup initial position
|
||||
const position: MenuPosition = { left: true, bottom: true };
|
||||
this.elem.style.left = left + "px"
|
||||
this.elem.style.top = bottom + "px"
|
||||
this.elem.style.left = left + "px";
|
||||
this.elem.style.top = bottom + "px";
|
||||
|
||||
// correct position if menu doesn't fit to viewport
|
||||
const menuPos = this.elem.getBoundingClientRect();
|
||||
@@ -148,24 +223,32 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
this.setState({ position });
|
||||
}, Animate.VISIBILITY_DELAY_MS);
|
||||
|
||||
open() {
|
||||
if (this.isOpen) return;
|
||||
open(): void {
|
||||
if (this.isOpen) {
|
||||
return;
|
||||
}
|
||||
this.props.open();
|
||||
this.refreshPosition();
|
||||
if (this.props.autoFocus) this.focusNextItem();
|
||||
if (this.props.autoFocus) {
|
||||
this.focusNextItem();
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (!this.isOpen) return;
|
||||
close(): void {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
this.props.close();
|
||||
}
|
||||
|
||||
toggle() {
|
||||
toggle(): void {
|
||||
this.isOpen ? this.close() : this.open();
|
||||
}
|
||||
|
||||
onKeyDown(evt: KeyboardEvent) {
|
||||
if (!this.isOpen) return;
|
||||
onKeyDown(evt: KeyboardEvent): void {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
switch (evt.code) {
|
||||
case "Escape":
|
||||
this.close();
|
||||
@@ -189,13 +272,17 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
}
|
||||
}
|
||||
|
||||
onWindowResize(evt: UIEvent) {
|
||||
if (!this.isOpen) return;
|
||||
onWindowResize(_evt: UIEvent): void {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
this.refreshPosition();
|
||||
}
|
||||
|
||||
onScrollOutside(evt: UIEvent) {
|
||||
if (!this.isOpen) return;
|
||||
onScrollOutside(evt: UIEvent): void {
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
const target = evt.target as HTMLElement;
|
||||
const { usePortal, closeOnScroll } = this.props;
|
||||
if (usePortal && closeOnScroll && !target.contains(this.elem)) {
|
||||
@@ -203,9 +290,13 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
}
|
||||
}
|
||||
|
||||
onClickOutside(evt: MouseEvent) {
|
||||
if (!this.props.closeOnClickOutside) return;
|
||||
if (!this.isOpen || evt.target === document.body) return;
|
||||
onClickOutside(evt: MouseEvent): void {
|
||||
if (!this.props.closeOnClickOutside) {
|
||||
return;
|
||||
}
|
||||
if (!this.isOpen || evt.target === document.body) {
|
||||
return;
|
||||
}
|
||||
const target = evt.target as HTMLElement;
|
||||
const clickInsideMenu = this.elem.contains(target);
|
||||
const clickOnOpener = this.opener && this.opener.contains(target);
|
||||
@@ -214,15 +305,15 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
}
|
||||
}
|
||||
|
||||
protected bindRef(elem: HTMLUListElement) {
|
||||
protected bindRef(elem: HTMLUListElement): void {
|
||||
this.elem = elem;
|
||||
}
|
||||
|
||||
protected bindItemRef(item: MenuItem, index: number) {
|
||||
protected bindItemRef(item: MenuItem, index: number): void {
|
||||
this.items[index] = item;
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
const { position } = this.props;
|
||||
let { className, usePortal } = this.props;
|
||||
className = cssNames('Menu', className, this.state.position || position, {
|
||||
@@ -251,12 +342,14 @@ export class Menu extends React.Component<MenuProps, State> {
|
||||
</Animate>
|
||||
</MenuContext.Provider>
|
||||
);
|
||||
if (usePortal === true) usePortal = document.body;
|
||||
if (usePortal === true) {
|
||||
usePortal = document.body;
|
||||
}
|
||||
return usePortal instanceof HTMLElement ? createPortal(menu, usePortal) : menu;
|
||||
}
|
||||
}
|
||||
|
||||
export function SubMenu(props: Partial<MenuProps>) {
|
||||
export function SubMenu(props: Partial<MenuProps>): JSX.Element {
|
||||
const { className, ...menuProps } = props;
|
||||
return (
|
||||
<Menu
|
||||
@@ -267,71 +360,6 @@ export function SubMenu(props: Partial<MenuProps>) {
|
||||
closeOnClickItem={false}
|
||||
{...menuProps}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export interface MenuItemProps extends React.HTMLProps<any> {
|
||||
icon?: string | Partial<IconProps>;
|
||||
disabled?: boolean;
|
||||
active?: boolean;
|
||||
spacer?: boolean;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
const defaultPropsMenuItem: Partial<MenuItemProps> = {
|
||||
onClick: noop,
|
||||
};
|
||||
|
||||
@autobind()
|
||||
export class MenuItem extends React.Component<MenuItemProps> {
|
||||
static defaultProps = defaultPropsMenuItem as object;
|
||||
static contextType = MenuContext;
|
||||
|
||||
public context: MenuContextValue;
|
||||
public elem: HTMLElement;
|
||||
|
||||
get isFocusable() {
|
||||
const { disabled, spacer } = this.props;
|
||||
return !(disabled || spacer);
|
||||
}
|
||||
|
||||
get isLink() {
|
||||
return !!this.props.href;
|
||||
}
|
||||
|
||||
onClick(evt: React.MouseEvent) {
|
||||
const menu = this.context;
|
||||
const { spacer, onClick } = this.props;
|
||||
if (spacer) return;
|
||||
onClick(evt);
|
||||
if (menu.props.closeOnClickItem && !evt.defaultPrevented) {
|
||||
menu.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected bindRef(elem: HTMLElement) {
|
||||
this.elem = elem;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, disabled, active, spacer, icon, children, ...props } = this.props;
|
||||
let iconProps: Partial<IconProps>;
|
||||
if (icon) {
|
||||
iconProps = {};
|
||||
if (typeof icon === "string") iconProps.material = icon;
|
||||
else Object.assign(iconProps, icon);
|
||||
}
|
||||
const elemProps: React.HTMLProps<any> = {
|
||||
tabIndex: this.isFocusable ? 0 : -1,
|
||||
...props,
|
||||
className: cssNames("MenuItem", className, { disabled, active, spacer }),
|
||||
onClick: this.onClick,
|
||||
children: icon ? <><Icon {...iconProps}/> {children}</> : children,
|
||||
ref: this.bindRef,
|
||||
}
|
||||
if (this.isLink) {
|
||||
return <a {...elemProps}/>
|
||||
}
|
||||
return <li {...elemProps}/>
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user