UI for enabling/disabling extensions (#1208)

* Extensions page and menu item

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Basic extension list view

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Adding get userExtensions filter

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Using WizardLayout at extension page

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Adding search to extension page

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* Few style fixes

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>

* clean up

Signed-off-by: Roman <ixrock@gmail.com>

* added folder-icon to open extensions in finder, refactoring

Signed-off-by: Roman <ixrock@gmail.com>

* remove export warnings in dev:main, tooltip.getPosition() fix

Signed-off-by: Roman <ixrock@gmail.com>

* refactoring base lens-extension.ts, added `isBundled` flag

Signed-off-by: Roman <ixrock@gmail.com>

* added enabled/disable buttons

Signed-off-by: Roman <ixrock@gmail.com>

* auto enable/disable extensions -- part 1

Signed-off-by: Roman <ixrock@gmail.com>

* auto enable/disable extensions -- part 2

Signed-off-by: Roman <ixrock@gmail.com>

* auto enable/disable extensions -- part 3

Signed-off-by: Roman <ixrock@gmail.com>

* auto enable/disable extensions -- part 4

Signed-off-by: Roman <ixrock@gmail.com>

* refactoring & fixes

Signed-off-by: Roman <ixrock@gmail.com>

* fix: use page-layout with fullsize viewport

Signed-off-by: Roman <ixrock@gmail.com>

Co-authored-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
RomanandAlex Andreev authored and GitHub committed 2020-11-04 19:25:36 +02:00
1 parent d5214e47c1
commit f9578ba407
17 files changed
+376 -191

No files matched your search

@@ -0,0 +1,8 @@
import { RouteProps } from "react-router";
import { buildURL } from "../../../common/utils/buildUrl";
export const extensionsRoute: RouteProps = {
path: "/extensions"
}
export const extensionsURL = buildURL(extensionsRoute.path)
@@ -0,0 +1,35 @@
.Extensions {
--width: 100%;
--max-width: auto;
.extension {
--flex-gap: $padding / 3;
padding: $padding $padding * 2;
background: $colorVague;
border-radius: $radius;
}
.extensions-path {
word-break: break-all;
}
.WizardLayout {
padding: 0;
.info-col {
flex: 0.6;
align-self: flex-start;
}
}
.SearchInput {
margin-top: $margin / 2;
margin-bottom: $margin * 2;
max-width: none;
> label {
padding: $padding $padding * 2;
border-radius: $radius;
}
}
}
@@ -0,0 +1,112 @@
import "./extensions.scss";
import { shell } from "electron";
import React from "react";
import { computed, observable } from "mobx";
import { observer } from "mobx-react";
import { t, Trans } from "@lingui/macro";
import { _i18n } from "../../i18n";
import { Button } from "../button";
import { WizardLayout } from "../layout/wizard-layout";
import { Input } from "../input";
import { Icon } from "../icon";
import { PageLayout } from "../layout/page-layout";
import { extensionLoader } from "../../../extensions/extension-loader";
import { extensionManager } from "../../../extensions/extension-manager";
@observer
export class Extensions extends React.Component {
@observable search = ""
@computed get extensions() {
const searchText = this.search.toLowerCase();
return extensionLoader.userExtensions.filter(({ name, description }) => {
return [
name.toLowerCase().includes(searchText),
description.toLowerCase().includes(searchText),
].some(v => v)
})
}
get extensionsPath() {
return extensionManager.localFolderPath;
}
renderInfo() {
return (
<div className="flex column gaps">
<h2>Lens Extension API</h2>
<div>
The Extensions API in Lens allows users to customize and enhance the Lens experience by creating their own menus or page content that is extended from the existing pages. Many of the core
features of Lens are built as extensions and use the same Extension API.
</div>
<div>
Extensions loaded from:
<div className="extensions-path flex inline">
<code>{this.extensionsPath}</code>
<Icon
material="folder"
tooltip="Open folder"
onClick={() => shell.openPath(this.extensionsPath)}
/>
</div>
</div>
<div>
Check out documentation to <a href="https://docs.k8slens.dev/" target="_blank">learn more</a>
</div>
</div>
)
}
renderExtensions() {
const { extensions, extensionsPath, search } = this;
if (!extensions.length) {
return (
<div className="flex align-center box grow justify-center gaps">
{search && <Trans>No search results found</Trans>}
{!search && <p><Trans>There are no extensions in</Trans> <code>{extensionsPath}</code></p>}
</div>
)
}
return extensions.map(ext => {
const { id, name, description, isEnabled } = ext;
return (
<div key={id} className="extension flex gaps align-center">
<div className="box grow flex column gaps">
<div className="package">
Name: <code className="name">{name}</code>
</div>
<div>
Description: <span className="text-secondary">{description}</span>
</div>
</div>
{!isEnabled && (
<Button plain active onClick={() => ext.enable()}>Enable</Button>
)}
{isEnabled && (
<Button accent onClick={() => ext.disable()}>Disable</Button>
)}
</div>
)
})
}
render() {
return (
<PageLayout showOnTop className="Extensions" header={<h2>Extensions</h2>}>
<WizardLayout infoPanel={this.renderInfo()}>
<Input
autoFocus
theme="round-black"
className="SearchInput"
placeholder={_i18n._(t`Search extensions`)}
value={this.search}
onChange={(value) => this.search = value}
/>
<div className="extension-list flex column gaps">
{this.renderExtensions()}
</div>
</WizardLayout>
</PageLayout>
);
}
}
@@ -0,0 +1,2 @@
export * from "./extensions.route"
export * from "./extensions"
@@ -16,6 +16,7 @@ import { clusterViewRoute, clusterViewURL } from "./cluster-view.route";
import { clusterStore } from "../../../common/cluster-store";
import { hasLoadedView, initView, lensViews, refreshViews } from "./lens-views";
import { globalPageRegistry } from "../../../extensions/registries/page-registry";
import { Extensions, extensionsRoute } from "../+extensions";
import { getMatchedClusterId } from "../../navigation";
@observer
@@ -63,6 +64,7 @@ export class ClusterManager extends React.Component {
<Switch>
<Route component={LandingPage} {...landingRoute} />
<Route component={Preferences} {...preferencesRoute} />
<Route component={Extensions} {...extensionsRoute} />
<Route component={Workspaces} {...workspacesRoute} />
<Route component={AddCluster} {...addClusterRoute} />
<Route component={ClusterView} {...clusterViewRoute} />
@@ -1,5 +1,8 @@
.PageLayout {
$spacing: $padding * 2;
--width: 60%;
--max-width: 1000px;
--min-width: 570px;
position: relative;
height: 100%;
@@ -26,12 +29,15 @@
> .content-wrapper {
@include custom-scrollbar-themed;
padding: $spacing * 2;
display: flex;
flex-direction: column;
> .content {
flex: 1;
margin: 0 auto;
width: 60%;
min-width: 570px;
max-width: 1000px;
width: var(--width);
min-width: var(--min-width);
max-width: var(--max-width);
}
}
@@ -167,7 +167,6 @@ export class Tooltip extends React.Component<TooltipProps> {
top = topCenter;
break;
case "top_right":
default:
left = targetBounds.right - tooltipBounds.width;
top = topCenter;
break;