mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge remote-tracking branch 'origin/master' into react-18-upgrade
This commit is contained in:
commit
f3b06cfc76
@ -3,15 +3,18 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
import { CatalogCategory, CatalogCategorySpec } from "../catalog";
|
import { CatalogCategory, CatalogCategorySpec } from "../catalog";
|
||||||
|
|
||||||
class TestCatalogCategory extends CatalogCategory {
|
class TestCatalogCategoryWithoutBadge extends CatalogCategory {
|
||||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||||
public readonly kind = "CatalogCategory";
|
public readonly kind = "CatalogCategory";
|
||||||
|
|
||||||
public metadata = {
|
public metadata = {
|
||||||
name: "Test Category",
|
name: "Test Category",
|
||||||
icon: "",
|
icon: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
public spec: CatalogCategorySpec = {
|
public spec: CatalogCategorySpec = {
|
||||||
group: "entity.k8slens.dev",
|
group: "entity.k8slens.dev",
|
||||||
versions: [],
|
versions: [],
|
||||||
@ -21,10 +24,28 @@ class TestCatalogCategory extends CatalogCategory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TestCatalogCategoryWithBadge extends TestCatalogCategoryWithoutBadge {
|
||||||
|
getBadge() {
|
||||||
|
return (<div>Test Badge</div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe("CatalogCategory", () => {
|
describe("CatalogCategory", () => {
|
||||||
it("returns name", () => {
|
it("returns name", () => {
|
||||||
const category = new TestCatalogCategory();
|
const category = new TestCatalogCategoryWithoutBadge();
|
||||||
|
|
||||||
expect(category.getName()).toEqual("Test Category");
|
expect(category.getName()).toEqual("Test Category");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("doesn't return badge by default", () => {
|
||||||
|
const category = new TestCatalogCategoryWithoutBadge();
|
||||||
|
|
||||||
|
expect(category.getBadge()).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a badge", () => {
|
||||||
|
const category = new TestCatalogCategoryWithBadge();
|
||||||
|
|
||||||
|
expect(category.getBadge()).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@ -180,6 +180,15 @@ export abstract class CatalogCategory extends (EventEmitter as new () => TypedEm
|
|||||||
return this.metadata.name;
|
return this.metadata.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the badge of this category.
|
||||||
|
* Defaults to no badge.
|
||||||
|
* The badge is displayed next to the Category name in the Catalog Category menu
|
||||||
|
*/
|
||||||
|
public getBadge(): React.ReactNode {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a filter for menu items of catalogAddMenu
|
* Add a filter for menu items of catalogAddMenu
|
||||||
* @param fn The function that should return a truthy value if that menu item should be displayed
|
* @param fn The function that should return a truthy value if that menu item should be displayed
|
||||||
|
|||||||
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import React from "react";
|
||||||
|
import "@testing-library/jest-dom/extend-expect";
|
||||||
|
import { CatalogCategory, CatalogCategorySpec } from "../../../../common/catalog";
|
||||||
|
import { CatalogCategoryLabel } from "../catalog-category-label";
|
||||||
|
|
||||||
|
class CatalogCategoryWithoutBadge extends CatalogCategory {
|
||||||
|
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||||
|
public readonly kind = "CatalogCategory";
|
||||||
|
|
||||||
|
public metadata = {
|
||||||
|
name: "Test Category",
|
||||||
|
icon: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
public spec: CatalogCategorySpec = {
|
||||||
|
group: "entity.k8slens.dev",
|
||||||
|
versions: [],
|
||||||
|
names: {
|
||||||
|
kind: "Test",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class CatalogCategoryWithBadge extends CatalogCategoryWithoutBadge {
|
||||||
|
getBadge() {
|
||||||
|
return (<div>Test Badge</div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("CatalogCategoryLabel", () => {
|
||||||
|
it("renders without a badge", async () => {
|
||||||
|
const category = new CatalogCategoryWithoutBadge();
|
||||||
|
|
||||||
|
render(<CatalogCategoryLabel category={category}/>);
|
||||||
|
|
||||||
|
expect(await screen.findByText("Test Category")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders with a badge", async () => {
|
||||||
|
const category = new CatalogCategoryWithBadge();
|
||||||
|
|
||||||
|
render(<CatalogCategoryLabel category={category}/>);
|
||||||
|
|
||||||
|
expect(await screen.findByText("Test Category")).toBeInTheDocument();
|
||||||
|
expect(await screen.findByText("Test Badge")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
24
src/renderer/components/+catalog/catalog-category-label.tsx
Normal file
24
src/renderer/components/+catalog/catalog-category-label.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import React from "react";
|
||||||
|
import type { CatalogCategory } from "../../../common/catalog/catalog-entity";
|
||||||
|
|
||||||
|
interface CatalogCategoryLabelProps {
|
||||||
|
category: CatalogCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display label for Catalog Category for the Catalog menu
|
||||||
|
*/
|
||||||
|
export const CatalogCategoryLabel = ({ category }: CatalogCategoryLabelProps) => {
|
||||||
|
const badge = category.getBadge();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex">
|
||||||
|
<div>{category.metadata.name}</div>
|
||||||
|
{badge ? (<div className="flex items-center">{badge}</div>) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -14,6 +14,7 @@ import { StylesProvider } from "@material-ui/core";
|
|||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import type { CatalogCategory } from "../../api/catalog-entity";
|
import type { CatalogCategory } from "../../api/catalog-entity";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import { CatalogCategoryLabel } from "./catalog-category-label";
|
||||||
|
|
||||||
export interface CatalogMenuProps {
|
export interface CatalogMenuProps {
|
||||||
activeItem: string;
|
activeItem: string;
|
||||||
@ -66,7 +67,7 @@ export const CatalogMenu = observer((props: CatalogMenuProps) => {
|
|||||||
icon={getCategoryIcon(category)}
|
icon={getCategoryIcon(category)}
|
||||||
key={category.getId()}
|
key={category.getId()}
|
||||||
nodeId={category.getId()}
|
nodeId={category.getId()}
|
||||||
label={category.metadata.name}
|
label={<CatalogCategoryLabel category={category}/>}
|
||||||
data-testid={`${category.getId()}-tab`}
|
data-testid={`${category.getId()}-tab`}
|
||||||
onClick={() => props.onItemClick(category.getId())}
|
onClick={() => props.onItemClick(category.getId())}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -83,19 +83,23 @@ export class Animate extends React.Component<AnimateProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (!this.isVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const { name, enterDuration, leaveDuration } = this.props;
|
const { name, enterDuration, leaveDuration } = this.props;
|
||||||
const contentElem = this.contentElem;
|
const contentElem = this.contentElem;
|
||||||
const durations = {
|
const cssVarsForAnimation = {
|
||||||
"--enter-duration": `${enterDuration}ms`,
|
"--enter-duration": `${enterDuration}ms`,
|
||||||
"--leave-duration": `${leaveDuration}ms`,
|
"--leave-duration": `${leaveDuration}ms`,
|
||||||
} as React.CSSProperties;
|
} as React.CSSProperties;
|
||||||
|
|
||||||
return React.cloneElement(contentElem, {
|
return React.cloneElement(contentElem, {
|
||||||
className: cssNames("Animate", name, contentElem.props.className, this.statusClassName),
|
className: cssNames("Animate", name, contentElem.props.className, this.statusClassName),
|
||||||
children: this.isVisible ? contentElem.props.children : null,
|
children: contentElem.props.children,
|
||||||
style: {
|
style: {
|
||||||
...contentElem.props.style,
|
...contentElem.props.style,
|
||||||
...durations,
|
...cssVarsForAnimation,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,9 +44,14 @@ const NonInjectedHotbarSelector = observer(({ hotbar, hotbarStore, openCommandOv
|
|||||||
tooltipTimeout.current = setTimeout(() => setTooltipVisible(false), 1500);
|
tooltipTimeout.current = setTimeout(() => setTooltipVisible(false), 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onArrowClick(switchTo: () => void) {
|
function onPrevClick() {
|
||||||
onTooltipShow();
|
onTooltipShow();
|
||||||
switchTo();
|
hotbarStore.switchToPrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNextClick() {
|
||||||
|
onTooltipShow();
|
||||||
|
hotbarStore.switchToNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseEvent(event: React.MouseEvent) {
|
function onMouseEvent(event: React.MouseEvent) {
|
||||||
@ -56,11 +61,7 @@ const NonInjectedHotbarSelector = observer(({ hotbar, hotbarStore, openCommandOv
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.HotbarSelector}>
|
<div className={styles.HotbarSelector}>
|
||||||
<Icon
|
<Icon material="play_arrow" className={cssNames(styles.Icon, styles.previous)} onClick={onPrevClick}/>
|
||||||
material="play_arrow"
|
|
||||||
className={cssNames(styles.Icon, styles.previous)}
|
|
||||||
onClick={() => onArrowClick(hotbarStore.switchToPrevious)}
|
|
||||||
/>
|
|
||||||
<div className={styles.HotbarIndex}>
|
<div className={styles.HotbarIndex}>
|
||||||
<Badge
|
<Badge
|
||||||
id="hotbarIndex"
|
id="hotbarIndex"
|
||||||
@ -79,7 +80,7 @@ const NonInjectedHotbarSelector = observer(({ hotbar, hotbarStore, openCommandOv
|
|||||||
{hotbar.name}
|
{hotbar.name}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<Icon material="play_arrow" className={styles.Icon} onClick={() => onArrowClick(hotbarStore.switchToNext)} />
|
<Icon material="play_arrow" className={styles.Icon} onClick={onNextClick} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -37,10 +37,6 @@ exports[`kube-object-menu given kube object renders 1`] = `
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
|
|
||||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
|
||||||
/>
|
|
||||||
</body>
|
</body>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@ -16,58 +16,6 @@ exports[`kube-object-status-icon given info and warning statuses are present, wh
|
|||||||
<div />
|
<div />
|
||||||
</i>
|
</i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="Tooltip narrow formatter"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="KubeObjectStatusTooltip"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="level warning"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="title"
|
|
||||||
>
|
|
||||||
Warning
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="status msg"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
Some warning status for some-name
|
|
||||||
|
|
||||||
<span
|
|
||||||
class="age"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
2d
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="level info"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="title"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="status msg"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
Some info status for some-name
|
|
||||||
|
|
||||||
<span
|
|
||||||
class="age"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
2d
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -87,36 +35,6 @@ exports[`kube-object-status-icon given level "critical" status, when rendered, r
|
|||||||
<div />
|
<div />
|
||||||
</i>
|
</i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="Tooltip narrow formatter"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="KubeObjectStatusTooltip"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="level error"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="title"
|
|
||||||
>
|
|
||||||
Critical
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="status msg"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
Some critical status for some-name
|
|
||||||
|
|
||||||
<span
|
|
||||||
class="age"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
2d
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -136,36 +54,6 @@ exports[`kube-object-status-icon given level "info" status, when rendered, rende
|
|||||||
<div />
|
<div />
|
||||||
</i>
|
</i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="Tooltip narrow formatter"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="KubeObjectStatusTooltip"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="level info"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="title"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="status msg"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
Some info status for some-name
|
|
||||||
|
|
||||||
<span
|
|
||||||
class="age"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
2d
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -185,36 +73,6 @@ exports[`kube-object-status-icon given level "warning" status, when rendered, re
|
|||||||
<div />
|
<div />
|
||||||
</i>
|
</i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="Tooltip narrow formatter"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="KubeObjectStatusTooltip"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="level warning"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="title"
|
|
||||||
>
|
|
||||||
Warning
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="status msg"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
Some warning status for some-name
|
|
||||||
|
|
||||||
<span
|
|
||||||
class="age"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
2d
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -254,79 +112,5 @@ exports[`kube-object-status-icon given status for all levels is present, when re
|
|||||||
<div />
|
<div />
|
||||||
</i>
|
</i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="Tooltip narrow formatter"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="KubeObjectStatusTooltip"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="level error"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="title"
|
|
||||||
>
|
|
||||||
Critical
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="status msg"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
Some critical status for some-name
|
|
||||||
|
|
||||||
<span
|
|
||||||
class="age"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
2d
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="level warning"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="title"
|
|
||||||
>
|
|
||||||
Warning
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="status msg"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
Some warning status for some-name
|
|
||||||
|
|
||||||
<span
|
|
||||||
class="age"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
2d
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="level info"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="title"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="status msg"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
Some info status for some-name
|
|
||||||
|
|
||||||
<span
|
|
||||||
class="age"
|
|
||||||
>
|
|
||||||
·
|
|
||||||
2d
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -93,7 +93,6 @@ export class Menu extends React.Component<MenuProps, State> {
|
|||||||
this.opener.addEventListener(this.props.toggleEvent, this.toggle);
|
this.opener.addEventListener(this.props.toggleEvent, this.toggle);
|
||||||
this.opener.addEventListener("keydown", this.onKeyDown);
|
this.opener.addEventListener("keydown", this.onKeyDown);
|
||||||
}
|
}
|
||||||
this.elem.addEventListener("keydown", this.onKeyDown);
|
|
||||||
window.addEventListener("resize", this.onWindowResize);
|
window.addEventListener("resize", this.onWindowResize);
|
||||||
window.addEventListener("click", this.onClickOutside, true);
|
window.addEventListener("click", this.onClickOutside, true);
|
||||||
window.addEventListener("scroll", this.onScrollOutside, true);
|
window.addEventListener("scroll", this.onScrollOutside, true);
|
||||||
@ -106,7 +105,6 @@ export class Menu extends React.Component<MenuProps, State> {
|
|||||||
this.opener.removeEventListener(this.props.toggleEvent, this.toggle);
|
this.opener.removeEventListener(this.props.toggleEvent, this.toggle);
|
||||||
this.opener.removeEventListener("keydown", this.onKeyDown);
|
this.opener.removeEventListener("keydown", this.onKeyDown);
|
||||||
}
|
}
|
||||||
this.elem.removeEventListener("keydown", this.onKeyDown);
|
|
||||||
window.removeEventListener("resize", this.onWindowResize);
|
window.removeEventListener("resize", this.onWindowResize);
|
||||||
window.removeEventListener("click", this.onClickOutside, true);
|
window.removeEventListener("click", this.onClickOutside, true);
|
||||||
window.removeEventListener("scroll", this.onScrollOutside, true);
|
window.removeEventListener("scroll", this.onScrollOutside, true);
|
||||||
@ -218,7 +216,7 @@ export class Menu extends React.Component<MenuProps, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown(evt: KeyboardEvent) {
|
onKeyDown(evt: React.KeyboardEvent | KeyboardEvent) {
|
||||||
if (!this.isOpen) return;
|
if (!this.isOpen) return;
|
||||||
|
|
||||||
switch (evt.code) {
|
switch (evt.code) {
|
||||||
@ -330,6 +328,7 @@ export class Menu extends React.Component<MenuProps, State> {
|
|||||||
left: this.state?.menuStyle?.left,
|
left: this.state?.menuStyle?.left,
|
||||||
top: this.state?.menuStyle?.top,
|
top: this.state?.menuStyle?.top,
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={this.onKeyDown}
|
||||||
>
|
>
|
||||||
{menuItems}
|
{menuItems}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import React from "react";
|
|||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { boundMethod, cssNames, IClassName } from "../../utils";
|
import { boundMethod, cssNames, IClassName } from "../../utils";
|
||||||
import { observable, makeObservable } from "mobx";
|
import { observable, makeObservable, action } from "mobx";
|
||||||
|
|
||||||
export enum TooltipPosition {
|
export enum TooltipPosition {
|
||||||
TOP = "top",
|
TOP = "top",
|
||||||
@ -54,7 +54,8 @@ export class Tooltip extends React.Component<TooltipProps> {
|
|||||||
|
|
||||||
@observable.ref elem: HTMLElement;
|
@observable.ref elem: HTMLElement;
|
||||||
@observable activePosition: TooltipPosition;
|
@observable activePosition: TooltipPosition;
|
||||||
@observable isVisible = false;
|
@observable isVisible = this.props.visible ?? false;
|
||||||
|
@observable isContentVisible = false; // animation manager
|
||||||
|
|
||||||
constructor(props: TooltipProps) {
|
constructor(props: TooltipProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -87,15 +88,16 @@ export class Tooltip extends React.Component<TooltipProps> {
|
|||||||
this.hoverTarget.removeEventListener("mouseleave", this.onLeaveTarget);
|
this.hoverTarget.removeEventListener("mouseleave", this.onLeaveTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@action.bound
|
||||||
protected onEnterTarget() {
|
protected onEnterTarget() {
|
||||||
this.isVisible = true;
|
this.isVisible = true;
|
||||||
this.refreshPosition();
|
requestAnimationFrame(action(() => this.isContentVisible = true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@action.bound
|
||||||
protected onLeaveTarget() {
|
protected onLeaveTarget() {
|
||||||
this.isVisible = false;
|
this.isVisible = false;
|
||||||
|
this.isContentVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
@ -103,6 +105,10 @@ export class Tooltip extends React.Component<TooltipProps> {
|
|||||||
const { preferredPositions } = this.props;
|
const { preferredPositions } = this.props;
|
||||||
const { elem, targetElem } = this;
|
const { elem, targetElem } = this;
|
||||||
|
|
||||||
|
if (!elem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let positions = new Set<TooltipPosition>([
|
let positions = new Set<TooltipPosition>([
|
||||||
TooltipPosition.RIGHT,
|
TooltipPosition.RIGHT,
|
||||||
TooltipPosition.BOTTOM,
|
TooltipPosition.BOTTOM,
|
||||||
@ -150,6 +156,10 @@ export class Tooltip extends React.Component<TooltipProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected setPosition(pos: { left: number; top: number }) {
|
protected setPosition(pos: { left: number; top: number }) {
|
||||||
|
if (!this.elem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const elemStyle = this.elem.style;
|
const elemStyle = this.elem.style;
|
||||||
|
|
||||||
elemStyle.left = `${pos.left}px`;
|
elemStyle.left = `${pos.left}px`;
|
||||||
@ -214,13 +224,17 @@ export class Tooltip extends React.Component<TooltipProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { style, formatters, usePortal, children, visible } = this.props;
|
if (!this.isVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { style, formatters, usePortal, children } = this.props;
|
||||||
const className = cssNames("Tooltip", this.props.className, formatters, this.activePosition, {
|
const className = cssNames("Tooltip", this.props.className, formatters, this.activePosition, {
|
||||||
visible: visible ?? this.isVisible,
|
visible: this.isContentVisible,
|
||||||
formatter: !!formatters,
|
formatter: !!formatters,
|
||||||
});
|
});
|
||||||
const tooltip = (
|
const tooltip = (
|
||||||
<div className={className} style={style} ref={this.bindRef}>
|
<div className={className} style={style} ref={this.bindRef} role="tooltip">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"sidebarActiveColor": "#ffffff",
|
"sidebarActiveColor": "#ffffff",
|
||||||
"sidebarSubmenuActiveColor": "#ffffff",
|
"sidebarSubmenuActiveColor": "#ffffff",
|
||||||
"sidebarItemHoverBackground": "#3a3e44",
|
"sidebarItemHoverBackground": "#3a3e44",
|
||||||
|
"badgeBackgroundColor": "#ffba44",
|
||||||
"buttonPrimaryBackground": "#3d90ce",
|
"buttonPrimaryBackground": "#3d90ce",
|
||||||
"buttonDefaultBackground": "#414448",
|
"buttonDefaultBackground": "#414448",
|
||||||
"buttonLightBackground": "#f1f1f1",
|
"buttonLightBackground": "#f1f1f1",
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"sidebarSubmenuActiveColor": "#3d90ce",
|
"sidebarSubmenuActiveColor": "#3d90ce",
|
||||||
"sidebarBackground": "#e8e8e8",
|
"sidebarBackground": "#e8e8e8",
|
||||||
"sidebarItemHoverBackground": "#f0f2f5",
|
"sidebarItemHoverBackground": "#f0f2f5",
|
||||||
|
"badgeBackgroundColor": "#ffba44",
|
||||||
"buttonPrimaryBackground": "#3d90ce",
|
"buttonPrimaryBackground": "#3d90ce",
|
||||||
"buttonDefaultBackground": "#414448",
|
"buttonDefaultBackground": "#414448",
|
||||||
"buttonLightBackground": "#f1f1f1",
|
"buttonLightBackground": "#f1f1f1",
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
--sidebarActiveColor: #ffffff;
|
--sidebarActiveColor: #ffffff;
|
||||||
--sidebarSubmenuActiveColor: #ffffff;
|
--sidebarSubmenuActiveColor: #ffffff;
|
||||||
--sidebarItemHoverBackground: #3a3e44;
|
--sidebarItemHoverBackground: #3a3e44;
|
||||||
|
--badgeBackgroundColor: #ffba44;
|
||||||
--buttonPrimaryBackground: #3d90ce;
|
--buttonPrimaryBackground: #3d90ce;
|
||||||
--buttonDefaultBackground: #414448;
|
--buttonDefaultBackground: #414448;
|
||||||
--buttonLightBackground: #f1f1f1;
|
--buttonLightBackground: #f1f1f1;
|
||||||
@ -73,6 +74,8 @@
|
|||||||
--dockEditorComment: #808080;
|
--dockEditorComment: #808080;
|
||||||
--dockEditorActiveLineBackground: #3a3d41;
|
--dockEditorActiveLineBackground: #3a3d41;
|
||||||
--dockBadgeBackground: #36393e;
|
--dockBadgeBackground: #36393e;
|
||||||
|
--dockTabBorderColor: #43424d;
|
||||||
|
--dockTabActiveBackground: #3a3e45;
|
||||||
--logsBackground: #000000;
|
--logsBackground: #000000;
|
||||||
--logsForeground: #ffffff;
|
--logsForeground: #ffffff;
|
||||||
--logRowHoverBackground: #35373a;
|
--logRowHoverBackground: #35373a;
|
||||||
@ -125,7 +128,7 @@
|
|||||||
--inputControlHoverBorder: #474a4f;
|
--inputControlHoverBorder: #474a4f;
|
||||||
--lineProgressBackground: #414448;
|
--lineProgressBackground: #414448;
|
||||||
--radioActiveBackground: #36393e;
|
--radioActiveBackground: #36393e;
|
||||||
--menuActiveBackground: #36393e;
|
--menuActiveBackground: #3d90ce;
|
||||||
--menuSelectedOptionBgc: #36393e;
|
--menuSelectedOptionBgc: #36393e;
|
||||||
--canvasBackground: #24292e;
|
--canvasBackground: #24292e;
|
||||||
--scrollBarColor: #5f6064;
|
--scrollBarColor: #5f6064;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user