mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into logs-search
Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
commit
eb4435e69d
@ -17,11 +17,11 @@ There are many sites where you can vote, recommend, favorite and star us.
|
|||||||
|
|
||||||
Here are some nice blog posts and videos about our project for you to get some inspiration:
|
Here are some nice blog posts and videos about our project for you to get some inspiration:
|
||||||
|
|
||||||
[Onboard AWS EKS Cluster on Lens(Kubernetes IDE)](https://dev.to/himwad05/onboard-aws-eks-cluster-on-lens-kubernetes-ide-492l)
|
* [Onboard AWS EKS Cluster on Lens(Kubernetes IDE)](https://dev.to/himwad05/onboard-aws-eks-cluster-on-lens-kubernetes-ide-492l)
|
||||||
[Using Lens to Manage All Your Kubernetes Cluster](https://medium.com/@magicmagnate/using-lens-to-manage-all-your-kubernetes-cluster-c1ef88fdb476)
|
* [Using Lens to Manage All Your Kubernetes Cluster](https://medium.com/@magicmagnate/using-lens-to-manage-all-your-kubernetes-cluster-c1ef88fdb476)
|
||||||
[Kontena Lens - Beautiful Kubernetes UI](https://www.youtube.com/watch?v=YGgaiGdYfdI)
|
* [Kontena Lens - Beautiful Kubernetes UI](https://www.youtube.com/watch?v=YGgaiGdYfdI)
|
||||||
[Gerenciando Kubernetes com Lens e Octant](https://www.youtube.com/watch?v=h9ZqDelJLQQ)
|
* [Gerenciando Kubernetes com Lens e Octant](https://www.youtube.com/watch?v=h9ZqDelJLQQ)
|
||||||
[Walkthrough of Kubernetes IDE - Lens](https://www.youtube.com/watch?v=602aHZSdEfY)
|
* [Walkthrough of Kubernetes IDE - Lens](https://www.youtube.com/watch?v=602aHZSdEfY)
|
||||||
[LENS - Interfaz Gráfica para Kubernetes en 1 PASO.](https://www.youtube.com/watch?v=DFMKcR4BqwM)
|
* [LENS - Interfaz Gráfica para Kubernetes en 1 PASO.](https://www.youtube.com/watch?v=DFMKcR4BqwM)
|
||||||
|
|
||||||
Psst... If you have created some content around Lens, let us know!
|
Psst... If you have created some content around Lens, let us know!
|
||||||
|
|||||||
@ -35,7 +35,13 @@ nav:
|
|||||||
- Publishing Extensions: extensions/testing-and-publishing/publishing.md
|
- Publishing Extensions: extensions/testing-and-publishing/publishing.md
|
||||||
- Bundling Extensions: extensions/testing-and-publishing/bundling.md
|
- Bundling Extensions: extensions/testing-and-publishing/bundling.md
|
||||||
- API Reference: extensions/api/README.md
|
- API Reference: extensions/api/README.md
|
||||||
- Contributing: contributing/README.md
|
- Contributing:
|
||||||
|
- Overview: contributing/README.md
|
||||||
|
- Development: contributing/development.md
|
||||||
|
- Documentation: contributing/documentation.md
|
||||||
|
- Maintainers: contributing/maintainers.md
|
||||||
|
- Promotion: contributing/promotion.md
|
||||||
|
|
||||||
- FAQ: faq/README.md
|
- FAQ: faq/README.md
|
||||||
theme:
|
theme:
|
||||||
name: 'material'
|
name: 'material'
|
||||||
|
|||||||
11
package.json
11
package.json
@ -15,7 +15,7 @@
|
|||||||
"dev-build": "concurrently yarn:compile:*",
|
"dev-build": "concurrently yarn:compile:*",
|
||||||
"dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"",
|
"dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"",
|
||||||
"dev:main": "yarn compile:main --watch",
|
"dev:main": "yarn compile:main --watch",
|
||||||
"dev:renderer": "yarn compile:renderer --watch",
|
"dev:renderer": "yarn webpack-dev-server --config webpack.renderer.ts",
|
||||||
"dev:extension-types": "yarn compile:extension-types --watch",
|
"dev:extension-types": "yarn compile:extension-types --watch",
|
||||||
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
||||||
"compile:main": "webpack --config webpack.main.ts",
|
"compile:main": "webpack --config webpack.main.ts",
|
||||||
@ -268,6 +268,7 @@
|
|||||||
"@lingui/macro": "^3.0.0-13",
|
"@lingui/macro": "^3.0.0-13",
|
||||||
"@lingui/react": "^3.0.0-13",
|
"@lingui/react": "^3.0.0-13",
|
||||||
"@material-ui/core": "^4.10.1",
|
"@material-ui/core": "^4.10.1",
|
||||||
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
|
||||||
"@rollup/plugin-json": "^4.1.0",
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
"@testing-library/jest-dom": "^5.11.5",
|
"@testing-library/jest-dom": "^5.11.5",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
@ -279,6 +280,7 @@
|
|||||||
"@types/electron-window-state": "^2.0.34",
|
"@types/electron-window-state": "^2.0.34",
|
||||||
"@types/fs-extra": "^9.0.1",
|
"@types/fs-extra": "^9.0.1",
|
||||||
"@types/hapi": "^18.0.3",
|
"@types/hapi": "^18.0.3",
|
||||||
|
"@types/hard-source-webpack-plugin": "^1.0.1",
|
||||||
"@types/hoist-non-react-statics": "^3.3.1",
|
"@types/hoist-non-react-statics": "^3.3.1",
|
||||||
"@types/html-webpack-plugin": "^3.2.3",
|
"@types/html-webpack-plugin": "^3.2.3",
|
||||||
"@types/http-proxy": "^1.17.4",
|
"@types/http-proxy": "^1.17.4",
|
||||||
@ -315,6 +317,7 @@
|
|||||||
"@types/uuid": "^8.0.0",
|
"@types/uuid": "^8.0.0",
|
||||||
"@types/webdriverio": "^4.13.0",
|
"@types/webdriverio": "^4.13.0",
|
||||||
"@types/webpack": "^4.41.17",
|
"@types/webpack": "^4.41.17",
|
||||||
|
"@types/webpack-dev-server": "^3.11.1",
|
||||||
"@types/webpack-env": "^1.15.2",
|
"@types/webpack-env": "^1.15.2",
|
||||||
"@types/webpack-node-externals": "^1.7.1",
|
"@types/webpack-node-externals": "^1.7.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.0.0",
|
"@typescript-eslint/eslint-plugin": "^4.0.0",
|
||||||
@ -339,6 +342,7 @@
|
|||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
"flex.box": "^3.4.4",
|
"flex.box": "^3.4.4",
|
||||||
"fork-ts-checker-webpack-plugin": "^5.0.0",
|
"fork-ts-checker-webpack-plugin": "^5.0.0",
|
||||||
|
"hard-source-webpack-plugin": "^0.13.1",
|
||||||
"hoist-non-react-statics": "^3.3.2",
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
"html-webpack-plugin": "^4.3.0",
|
"html-webpack-plugin": "^4.3.0",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
@ -360,6 +364,7 @@
|
|||||||
"react": "^16.14.0",
|
"react": "^16.14.0",
|
||||||
"react-beautiful-dnd": "^13.0.0",
|
"react-beautiful-dnd": "^13.0.0",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
|
"react-refresh": "^0.9.0",
|
||||||
"react-router": "^5.2.0",
|
"react-router": "^5.2.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-select": "^3.1.0",
|
"react-select": "^3.1.0",
|
||||||
@ -376,11 +381,13 @@
|
|||||||
"ts-jest": "^26.1.0",
|
"ts-jest": "^26.1.0",
|
||||||
"ts-loader": "^7.0.5",
|
"ts-loader": "^7.0.5",
|
||||||
"ts-node": "^8.10.2",
|
"ts-node": "^8.10.2",
|
||||||
|
"type-fest": "^0.18.0",
|
||||||
"typeface-roboto": "^0.0.75",
|
"typeface-roboto": "^0.0.75",
|
||||||
"typescript": "^4.0.2",
|
"typescript": "^4.0.2",
|
||||||
"url-loader": "^4.1.0",
|
"url-loader": "^4.1.0",
|
||||||
"webpack": "^4.43.0",
|
"webpack": "^4.44.2",
|
||||||
"webpack-cli": "^3.3.11",
|
"webpack-cli": "^3.3.11",
|
||||||
|
"webpack-dev-server": "^3.11.0",
|
||||||
"webpack-node-externals": "^1.7.2",
|
"webpack-node-externals": "^1.7.2",
|
||||||
"xterm": "^4.6.0",
|
"xterm": "^4.6.0",
|
||||||
"xterm-addon-fit": "^0.4.0"
|
"xterm-addon-fit": "^0.4.0"
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export const mainDir = path.join(contextDir, "src/main");
|
|||||||
export const rendererDir = path.join(contextDir, "src/renderer");
|
export const rendererDir = path.join(contextDir, "src/renderer");
|
||||||
export const htmlTemplate = path.resolve(rendererDir, "template.html");
|
export const htmlTemplate = path.resolve(rendererDir, "template.html");
|
||||||
export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss");
|
export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss");
|
||||||
|
export const webpackDevServerPort = 9009
|
||||||
|
|
||||||
// Special runtime paths
|
// Special runtime paths
|
||||||
defineGlobal("__static", {
|
defineGlobal("__static", {
|
||||||
|
|||||||
@ -1,23 +1,38 @@
|
|||||||
// TODO: add more common re-usable UI components + refactor interfaces (Props -> ComponentProps)
|
// Common UI components
|
||||||
|
|
||||||
export * from "../../renderer/components/icon"
|
// layouts
|
||||||
export * from "../../renderer/components/checkbox"
|
|
||||||
export * from "../../renderer/components/tooltip"
|
|
||||||
export * from "../../renderer/components/button"
|
|
||||||
export * from "../../renderer/components/tabs"
|
|
||||||
export * from "../../renderer/components/badge"
|
|
||||||
export * from "../../renderer/components/layout/page-layout"
|
export * from "../../renderer/components/layout/page-layout"
|
||||||
|
export * from "../../renderer/components/layout/wizard-layout"
|
||||||
|
export * from "../../renderer/components/layout/tab-layout"
|
||||||
|
|
||||||
|
// form-controls
|
||||||
|
export * from "../../renderer/components/button"
|
||||||
|
export * from "../../renderer/components/checkbox"
|
||||||
|
export * from "../../renderer/components/radio"
|
||||||
|
export * from "../../renderer/components/select"
|
||||||
|
export * from "../../renderer/components/slider"
|
||||||
|
export * from "../../renderer/components/input/input"
|
||||||
|
|
||||||
|
// other components
|
||||||
|
export * from "../../renderer/components/icon"
|
||||||
|
export * from "../../renderer/components/tooltip"
|
||||||
|
export * from "../../renderer/components/tabs"
|
||||||
|
export * from "../../renderer/components/table"
|
||||||
|
export * from "../../renderer/components/badge"
|
||||||
export * from "../../renderer/components/drawer"
|
export * from "../../renderer/components/drawer"
|
||||||
|
export * from "../../renderer/components/dialog"
|
||||||
|
export * from "../../renderer/components/confirm-dialog";
|
||||||
|
export * from "../../renderer/components/line-progress"
|
||||||
|
export * from "../../renderer/components/menu"
|
||||||
|
export * from "../../renderer/components/notifications"
|
||||||
|
export * from "../../renderer/components/spinner"
|
||||||
|
export * from "../../renderer/components/stepper"
|
||||||
|
|
||||||
// kube helpers
|
// kube helpers
|
||||||
export { KubeObjectDetailsProps, KubeObjectMenuProps } from "../../renderer/components/kube-object"
|
export * from "../../renderer/components/kube-object"
|
||||||
export { KubeObjectMeta } from "../../renderer/components/kube-object/kube-object-meta"
|
export * from "../../renderer/components/+events/kube-event-details"
|
||||||
export { KubeObjectListLayout, KubeObjectListLayoutProps } from "../../renderer/components/kube-object/kube-object-list-layout";
|
|
||||||
export { KubeEventDetails } from "../../renderer/components/+events/kube-event-details"
|
|
||||||
|
|
||||||
// specific exports
|
// specific exports
|
||||||
export { ConfirmDialog } from "../../renderer/components/confirm-dialog";
|
export * from "../../renderer/components/status-brick";
|
||||||
export { MenuItem, SubMenu } from "../../renderer/components/menu";
|
|
||||||
export { StatusBrick } from "../../renderer/components/status-brick";
|
|
||||||
export { terminalStore, createTerminalTab } from "../../renderer/components/dock/terminal.store";
|
export { terminalStore, createTerminalTab } from "../../renderer/components/dock/terminal.store";
|
||||||
export { createPodLogsTab } from "../../renderer/components/dock/pod-logs.store";
|
export { createPodLogsTab } from "../../renderer/components/dock/pod-logs.store";
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import http from "http"
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import { readFile } from "fs-extra"
|
import { readFile } from "fs-extra"
|
||||||
import { Cluster } from "./cluster"
|
import { Cluster } from "./cluster"
|
||||||
import { apiPrefix, appName, publicPath } from "../common/vars";
|
import { apiPrefix, appName, publicPath, isDevelopment, webpackDevServerPort } from "../common/vars";
|
||||||
import { helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, watchRoute } from "./routes";
|
import { helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, watchRoute } from "./routes";
|
||||||
|
|
||||||
export interface RouterRequestOpts {
|
export interface RouterRequestOpts {
|
||||||
@ -94,23 +94,35 @@ export class Router {
|
|||||||
return mimeTypes[path.extname(filename).slice(1)] || "text/plain"
|
return mimeTypes[path.extname(filename).slice(1)] || "text/plain"
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleStaticFile(filePath: string, res: http.ServerResponse) {
|
async handleStaticFile(filePath: string, res: http.ServerResponse, req: http.IncomingMessage) {
|
||||||
const asset = path.join(__static, filePath);
|
const asset = path.join(__static, filePath);
|
||||||
try {
|
try {
|
||||||
|
const filename = path.basename(req.url);
|
||||||
|
// redirect requests to [appName].js, [appName].html /sockjs-node/ to webpack-dev-server (for hot-reload support)
|
||||||
|
const toWebpackDevServer = filename.includes(appName) || filename.includes('hot-update') || req.url.includes('sockjs-node');
|
||||||
|
if (isDevelopment && toWebpackDevServer) {
|
||||||
|
const redirectLocation = `http://localhost:${webpackDevServerPort}` + req.url;
|
||||||
|
res.statusCode = 307;
|
||||||
|
res.setHeader('Location', redirectLocation);
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const data = await readFile(asset);
|
const data = await readFile(asset);
|
||||||
res.setHeader("Content-Type", this.getMimeType(asset));
|
res.setHeader("Content-Type", this.getMimeType(asset));
|
||||||
res.write(data)
|
res.write(data);
|
||||||
res.end()
|
res.end();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.handleStaticFile(`${publicPath}/${appName}.html`, res);
|
this.handleStaticFile(`${publicPath}/${appName}.html`, res, req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected addRoutes() {
|
protected addRoutes() {
|
||||||
// Static assets
|
// Static assets
|
||||||
this.router.add({ method: 'get', path: '/{path*}' }, ({ params, response }: LensApiRequest) => {
|
this.router.add(
|
||||||
this.handleStaticFile(params.path, response);
|
{ method: 'get', path: '/{path*}' },
|
||||||
});
|
({ params, response, path, raw: { req }}: LensApiRequest) => {
|
||||||
|
this.handleStaticFile(params.path, response, req);
|
||||||
|
});
|
||||||
|
|
||||||
this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, kubeconfigRoute.routeServiceAccountRoute.bind(kubeconfigRoute))
|
this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, kubeconfigRoute.routeServiceAccountRoute.bind(kubeconfigRoute))
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,13 @@
|
|||||||
import type { KubeObjectStore } from "../kube-object.store";
|
import type { KubeObjectStore } from "../kube-object.store";
|
||||||
import type { KubeObjectDetailsProps, KubeObjectListLayoutProps, KubeObjectMenuProps } from "../components/kube-object";
|
|
||||||
import type React from "react";
|
|
||||||
|
|
||||||
import { observable } from "mobx";
|
import { action, observable } from "mobx";
|
||||||
import { autobind } from "../utils";
|
import { autobind } from "../utils";
|
||||||
import { KubeApi } from "./kube-api";
|
import { KubeApi } from "./kube-api";
|
||||||
|
|
||||||
export interface ApiComponents {
|
|
||||||
List?: React.ComponentType<KubeObjectListLayoutProps>;
|
|
||||||
Menu?: React.ComponentType<KubeObjectMenuProps>;
|
|
||||||
Details?: React.ComponentType<KubeObjectDetailsProps>;
|
|
||||||
}
|
|
||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
export class ApiManager {
|
export class ApiManager {
|
||||||
private apis = observable.map<string, KubeApi>();
|
private apis = observable.map<string, KubeApi>();
|
||||||
private stores = observable.map<KubeApi, KubeObjectStore>();
|
private stores = observable.map<KubeApi, KubeObjectStore>();
|
||||||
private views = observable.map<KubeApi, ApiComponents>();
|
|
||||||
|
|
||||||
getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) {
|
getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) {
|
||||||
if (typeof pathOrCallback === "string") {
|
if (typeof pathOrCallback === "string") {
|
||||||
@ -46,8 +37,11 @@ export class ApiManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerStore(api: KubeApi, store: KubeObjectStore) {
|
@action
|
||||||
this.stores.set(api, store);
|
registerStore(store: KubeObjectStore, apis: KubeApi[] = [store.api]) {
|
||||||
|
apis.forEach(api => {
|
||||||
|
this.stores.set(api, store);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getStore(api: string | KubeApi): KubeObjectStore {
|
getStore(api: string | KubeApi): KubeObjectStore {
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { kubeWatchApi } from "./kube-watch-api";
|
|||||||
import { apiManager } from "./api-manager";
|
import { apiManager } from "./api-manager";
|
||||||
import { createKubeApiURL, parseKubeApi } from "./kube-api-parse";
|
import { createKubeApiURL, parseKubeApi } from "./kube-api-parse";
|
||||||
import { apiKubePrefix, isDevelopment } from "../../common/vars";
|
import { apiKubePrefix, isDevelopment } from "../../common/vars";
|
||||||
import * as URL from "url"
|
|
||||||
|
|
||||||
export interface IKubeApiOptions<T extends KubeObject> {
|
export interface IKubeApiOptions<T extends KubeObject> {
|
||||||
apiBase?: string; // base api-path for listing all resources, e.g. "/api/v1/pods"
|
apiBase?: string; // base api-path for listing all resources, e.g. "/api/v1/pods"
|
||||||
|
|||||||
@ -107,4 +107,4 @@ export class ClusterStore extends KubeObjectStore<Cluster> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const clusterStore = new ClusterStore();
|
export const clusterStore = new ClusterStore();
|
||||||
apiManager.registerStore(clusterApi, clusterStore);
|
apiManager.registerStore(clusterStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class HPAStore extends KubeObjectStore<HorizontalPodAutoscaler> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const hpaStore = new HPAStore();
|
export const hpaStore = new HPAStore();
|
||||||
apiManager.registerStore(hpaApi, hpaStore);
|
apiManager.registerStore(hpaStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class ConfigMapsStore extends KubeObjectStore<ConfigMap> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const configMapsStore = new ConfigMapsStore();
|
export const configMapsStore = new ConfigMapsStore();
|
||||||
apiManager.registerStore(configMapApi, configMapsStore);
|
apiManager.registerStore(configMapsStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class PodDisruptionBudgetsStore extends KubeObjectStore<PodDisruptionBudg
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const podDisruptionBudgetsStore = new PodDisruptionBudgetsStore();
|
export const podDisruptionBudgetsStore = new PodDisruptionBudgetsStore();
|
||||||
apiManager.registerStore(pdbApi, podDisruptionBudgetsStore);
|
apiManager.registerStore(podDisruptionBudgetsStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class ResourceQuotasStore extends KubeObjectStore<ResourceQuota> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const resourceQuotaStore = new ResourceQuotasStore();
|
export const resourceQuotaStore = new ResourceQuotasStore();
|
||||||
apiManager.registerStore(resourceQuotaApi, resourceQuotaStore);
|
apiManager.registerStore(resourceQuotaStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class SecretsStore extends KubeObjectStore<Secret> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const secretsStore = new SecretsStore();
|
export const secretsStore = new SecretsStore();
|
||||||
apiManager.registerStore(secretsApi, secretsStore);
|
apiManager.registerStore(secretsStore);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { KubeObject } from "../../api/kube-object";
|
|||||||
import { ICRDRouteParams } from "./crd.route";
|
import { ICRDRouteParams } from "./crd.route";
|
||||||
import { autorun, computed } from "mobx";
|
import { autorun, computed } from "mobx";
|
||||||
import { crdStore } from "./crd.store";
|
import { crdStore } from "./crd.store";
|
||||||
import { SortingCallback } from "../table";
|
import { TableSortCallback } from "../table";
|
||||||
import { apiManager } from "../../api/api-manager";
|
import { apiManager } from "../../api/api-manager";
|
||||||
|
|
||||||
interface Props extends RouteComponentProps<ICRDRouteParams> {
|
interface Props extends RouteComponentProps<ICRDRouteParams> {
|
||||||
@ -50,7 +50,7 @@ export class CrdResources extends React.Component<Props> {
|
|||||||
if (!crd) return null;
|
if (!crd) return null;
|
||||||
const isNamespaced = crd.isNamespaced();
|
const isNamespaced = crd.isNamespaced();
|
||||||
const extraColumns = crd.getPrinterColumns(false); // Cols with priority bigger than 0 are shown in details
|
const extraColumns = crd.getPrinterColumns(false); // Cols with priority bigger than 0 are shown in details
|
||||||
const sortingCallbacks: { [sortBy: string]: SortingCallback } = {
|
const sortingCallbacks: { [sortBy: string]: TableSortCallback } = {
|
||||||
[sortBy.name]: (item: KubeObject) => item.getName(),
|
[sortBy.name]: (item: KubeObject) => item.getName(),
|
||||||
[sortBy.namespace]: (item: KubeObject) => item.getNs(),
|
[sortBy.namespace]: (item: KubeObject) => item.getNs(),
|
||||||
[sortBy.age]: (item: KubeObject) => item.metadata.creationTimestamp,
|
[sortBy.age]: (item: KubeObject) => item.metadata.creationTimestamp,
|
||||||
|
|||||||
@ -14,7 +14,7 @@ function initStore(crd: CustomResourceDefinition) {
|
|||||||
const api = apiManager.getApi(apiBase) || new KubeApi({ apiBase, kind, isNamespaced });
|
const api = apiManager.getApi(apiBase) || new KubeApi({ apiBase, kind, isNamespaced });
|
||||||
|
|
||||||
if (!apiManager.getStore(api)) {
|
if (!apiManager.getStore(api)) {
|
||||||
apiManager.registerStore(api, new CRDResourceStore(api));
|
apiManager.registerStore(new CRDResourceStore(api));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,4 +64,4 @@ export class CRDStore extends KubeObjectStore<CustomResourceDefinition> {
|
|||||||
|
|
||||||
export const crdStore = new CRDStore();
|
export const crdStore = new CRDStore();
|
||||||
|
|
||||||
apiManager.registerStore(crdApi, crdStore);
|
apiManager.registerStore(crdStore);
|
||||||
|
|||||||
@ -49,4 +49,4 @@ export class EventStore extends KubeObjectStore<KubeEvent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const eventStore = new EventStore();
|
export const eventStore = new EventStore();
|
||||||
apiManager.registerStore(eventApi, eventStore);
|
apiManager.registerStore(eventStore);
|
||||||
|
|||||||
@ -6,15 +6,14 @@ import { Trans } from "@lingui/macro";
|
|||||||
import { KubeObject } from "../../api/kube-object";
|
import { KubeObject } from "../../api/kube-object";
|
||||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import { Icon } from "../icon";
|
|
||||||
import { eventStore } from "./event.store";
|
import { eventStore } from "./event.store";
|
||||||
|
|
||||||
interface Props {
|
export interface KubeEventDetailsProps {
|
||||||
object: KubeObject;
|
object: KubeObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class KubeEventDetails extends React.Component<Props> {
|
export class KubeEventDetails extends React.Component<KubeEventDetailsProps> {
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
eventStore.loadAll();
|
eventStore.loadAll();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,4 +95,4 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const namespaceStore = new NamespaceStore();
|
export const namespaceStore = new NamespaceStore();
|
||||||
apiManager.registerStore(namespacesApi, namespaceStore);
|
apiManager.registerStore(namespaceStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class EndpointStore extends KubeObjectStore<Endpoint> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const endpointStore = new EndpointStore();
|
export const endpointStore = new EndpointStore();
|
||||||
apiManager.registerStore(endpointApi, endpointStore);
|
apiManager.registerStore(endpointStore);
|
||||||
|
|||||||
@ -19,4 +19,4 @@ export class IngressStore extends KubeObjectStore<Ingress> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ingressStore = new IngressStore();
|
export const ingressStore = new IngressStore();
|
||||||
apiManager.registerStore(ingressApi, ingressStore);
|
apiManager.registerStore(ingressStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class NetworkPolicyStore extends KubeObjectStore<NetworkPolicy> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const networkPolicyStore = new NetworkPolicyStore();
|
export const networkPolicyStore = new NetworkPolicyStore();
|
||||||
apiManager.registerStore(networkPolicyApi, networkPolicyStore);
|
apiManager.registerStore(networkPolicyStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class ServiceStore extends KubeObjectStore<Service> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const serviceStore = new ServiceStore();
|
export const serviceStore = new ServiceStore();
|
||||||
apiManager.registerStore(serviceApi, serviceStore);
|
apiManager.registerStore(serviceStore);
|
||||||
|
|||||||
@ -69,4 +69,4 @@ export class NodesStore extends KubeObjectStore<Node> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const nodesStore = new NodesStore()
|
export const nodesStore = new NodesStore()
|
||||||
apiManager.registerStore(nodesApi, nodesStore);
|
apiManager.registerStore(nodesStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class PodSecurityPoliciesStore extends KubeObjectStore<PodSecurityPolicy>
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const podSecurityPoliciesStore = new PodSecurityPoliciesStore()
|
export const podSecurityPoliciesStore = new PodSecurityPoliciesStore()
|
||||||
apiManager.registerStore(pspApi, podSecurityPoliciesStore);
|
apiManager.registerStore(podSecurityPoliciesStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class StorageClassStore extends KubeObjectStore<StorageClass> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const storageClassStore = new StorageClassStore();
|
export const storageClassStore = new StorageClassStore();
|
||||||
apiManager.registerStore(storageClassApi, storageClassStore);
|
apiManager.registerStore(storageClassStore);
|
||||||
|
|||||||
@ -20,4 +20,4 @@ export class VolumeClaimStore extends KubeObjectStore<PersistentVolumeClaim> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const volumeClaimStore = new VolumeClaimStore();
|
export const volumeClaimStore = new VolumeClaimStore();
|
||||||
apiManager.registerStore(pvcApi, volumeClaimStore);
|
apiManager.registerStore(volumeClaimStore);
|
||||||
|
|||||||
@ -9,4 +9,4 @@ export class PersistentVolumesStore extends KubeObjectStore<PersistentVolume> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const volumesStore = new PersistentVolumesStore();
|
export const volumesStore = new PersistentVolumesStore();
|
||||||
apiManager.registerStore(persistentVolumeApi, volumesStore);
|
apiManager.registerStore(volumesStore);
|
||||||
|
|||||||
@ -30,8 +30,7 @@ export class RoleBindingsStore extends KubeObjectStore<RoleBinding> {
|
|||||||
return Promise.all(
|
return Promise.all(
|
||||||
namespaces.map(namespace => roleBindingApi.list({ namespace }))
|
namespaces.map(namespace => roleBindingApi.list({ namespace }))
|
||||||
).then(items => items.flat())
|
).then(items => items.flat())
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return Promise.all([clusterRoleBindingApi.list(), roleBindingApi.list()])
|
return Promise.all([clusterRoleBindingApi.list(), roleBindingApi.list()])
|
||||||
.then(items => items.flat())
|
.then(items => items.flat())
|
||||||
}
|
}
|
||||||
@ -40,8 +39,7 @@ export class RoleBindingsStore extends KubeObjectStore<RoleBinding> {
|
|||||||
protected async createItem(params: { name: string; namespace?: string }, data?: Partial<RoleBinding>) {
|
protected async createItem(params: { name: string; namespace?: string }, data?: Partial<RoleBinding>) {
|
||||||
if (params.namespace) {
|
if (params.namespace) {
|
||||||
return roleBindingApi.create(params, data)
|
return roleBindingApi.create(params, data)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return clusterRoleBindingApi.create(params, data)
|
return clusterRoleBindingApi.create(params, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,8 +56,7 @@ export class RoleBindingsStore extends KubeObjectStore<RoleBinding> {
|
|||||||
newSubjects = uniqBy(currentSubjects.concat(addSubjects), ({ kind, name, namespace }) => {
|
newSubjects = uniqBy(currentSubjects.concat(addSubjects), ({ kind, name, namespace }) => {
|
||||||
return [kind, name, namespace].join("-");
|
return [kind, name, namespace].join("-");
|
||||||
})
|
})
|
||||||
}
|
} else if (removeSubjects) {
|
||||||
else if (removeSubjects) {
|
|
||||||
newSubjects = difference(currentSubjects, removeSubjects);
|
newSubjects = difference(currentSubjects, removeSubjects);
|
||||||
}
|
}
|
||||||
return this.update(roleBinding, {
|
return this.update(roleBinding, {
|
||||||
@ -71,5 +68,7 @@ export class RoleBindingsStore extends KubeObjectStore<RoleBinding> {
|
|||||||
|
|
||||||
export const roleBindingsStore = new RoleBindingsStore();
|
export const roleBindingsStore = new RoleBindingsStore();
|
||||||
|
|
||||||
apiManager.registerStore(roleBindingApi, roleBindingsStore);
|
apiManager.registerStore(roleBindingsStore, [
|
||||||
apiManager.registerStore(clusterRoleBindingApi, roleBindingsStore);
|
roleBindingApi,
|
||||||
|
clusterRoleBindingApi,
|
||||||
|
]);
|
||||||
|
|||||||
@ -28,8 +28,7 @@ export class RolesStore extends KubeObjectStore<Role> {
|
|||||||
return Promise.all(
|
return Promise.all(
|
||||||
namespaces.map(namespace => roleApi.list({ namespace }))
|
namespaces.map(namespace => roleApi.list({ namespace }))
|
||||||
).then(items => items.flat())
|
).then(items => items.flat())
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return Promise.all([clusterRoleApi.list(), roleApi.list()])
|
return Promise.all([clusterRoleApi.list(), roleApi.list()])
|
||||||
.then(items => items.flat())
|
.then(items => items.flat())
|
||||||
}
|
}
|
||||||
@ -38,8 +37,7 @@ export class RolesStore extends KubeObjectStore<Role> {
|
|||||||
protected async createItem(params: { name: string; namespace?: string }, data?: Partial<Role>) {
|
protected async createItem(params: { name: string; namespace?: string }, data?: Partial<Role>) {
|
||||||
if (params.namespace) {
|
if (params.namespace) {
|
||||||
return roleApi.create(params, data)
|
return roleApi.create(params, data)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return clusterRoleApi.create(params, data)
|
return clusterRoleApi.create(params, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,5 +45,7 @@ export class RolesStore extends KubeObjectStore<Role> {
|
|||||||
|
|
||||||
export const rolesStore = new RolesStore();
|
export const rolesStore = new RolesStore();
|
||||||
|
|
||||||
apiManager.registerStore(roleApi, rolesStore);
|
apiManager.registerStore(rolesStore, [
|
||||||
apiManager.registerStore(clusterRoleApi, rolesStore);
|
roleApi,
|
||||||
|
clusterRoleApi,
|
||||||
|
]);
|
||||||
@ -14,4 +14,4 @@ export class ServiceAccountsStore extends KubeObjectStore<ServiceAccount> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const serviceAccountsStore = new ServiceAccountsStore();
|
export const serviceAccountsStore = new ServiceAccountsStore();
|
||||||
apiManager.registerStore(serviceAccountsApi, serviceAccountsStore);
|
apiManager.registerStore(serviceAccountsStore);
|
||||||
|
|||||||
@ -30,4 +30,4 @@ export class CronJobStore extends KubeObjectStore<CronJob> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const cronJobStore = new CronJobStore();
|
export const cronJobStore = new CronJobStore();
|
||||||
apiManager.registerStore(cronJobApi, cronJobStore);
|
apiManager.registerStore(cronJobStore);
|
||||||
|
|||||||
@ -43,4 +43,4 @@ export class DaemonSetStore extends KubeObjectStore<DaemonSet> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const daemonSetStore = new DaemonSetStore();
|
export const daemonSetStore = new DaemonSetStore();
|
||||||
apiManager.registerStore(daemonSetApi, daemonSetStore);
|
apiManager.registerStore(daemonSetStore);
|
||||||
|
|||||||
@ -50,4 +50,4 @@ export class DeploymentStore extends KubeObjectStore<Deployment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const deploymentStore = new DeploymentStore();
|
export const deploymentStore = new DeploymentStore();
|
||||||
apiManager.registerStore(deploymentApi, deploymentStore);
|
apiManager.registerStore(deploymentStore);
|
||||||
|
|||||||
@ -42,4 +42,4 @@ export class JobStore extends KubeObjectStore<Job> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const jobStore = new JobStore();
|
export const jobStore = new JobStore();
|
||||||
apiManager.registerStore(jobApi, jobStore);
|
apiManager.registerStore(jobStore);
|
||||||
|
|||||||
@ -76,4 +76,4 @@ export class PodsStore extends KubeObjectStore<Pod> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const podsStore = new PodsStore();
|
export const podsStore = new PodsStore();
|
||||||
apiManager.registerStore(podsApi, podsStore);
|
apiManager.registerStore(podsStore);
|
||||||
|
|||||||
@ -31,4 +31,4 @@ export class ReplicaSetStore extends KubeObjectStore<ReplicaSet> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const replicaSetStore = new ReplicaSetStore();
|
export const replicaSetStore = new ReplicaSetStore();
|
||||||
apiManager.registerStore(replicaSetApi, replicaSetStore);
|
apiManager.registerStore(replicaSetStore);
|
||||||
|
|||||||
@ -42,4 +42,4 @@ export class StatefulSetStore extends KubeObjectStore<StatefulSet> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const statefulSetStore = new StatefulSetStore();
|
export const statefulSetStore = new StatefulSetStore();
|
||||||
apiManager.registerStore(statefulSetApi, statefulSetStore);
|
apiManager.registerStore(statefulSetStore);
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { Icon } from "../icon";
|
|||||||
import { Input } from "../input";
|
import { Input } from "../input";
|
||||||
import { cssNames, prevDefault } from "../../utils";
|
import { cssNames, prevDefault } from "../../utils";
|
||||||
import { Button } from "../button";
|
import { Button } from "../button";
|
||||||
import { isRequired, Validator } from "../input/input_validators";
|
import { isRequired, InputValidator } from "../input/input_validators";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Workspaces extends React.Component {
|
export class Workspaces extends React.Component {
|
||||||
@ -122,7 +122,7 @@ export class Workspaces extends React.Component {
|
|||||||
editing: isEditing,
|
editing: isEditing,
|
||||||
default: isDefault,
|
default: isDefault,
|
||||||
});
|
});
|
||||||
const existenceValidator: Validator = {
|
const existenceValidator: InputValidator = {
|
||||||
message: () => `Workspace '${name}' already exists`,
|
message: () => `Workspace '${name}' already exists`,
|
||||||
validate: value => !workspaceStore.getByName(value.trim())
|
validate: value => !workspaceStore.getByName(value.trim())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,13 +4,13 @@ import React from "react";
|
|||||||
import { cssNames } from "../../utils/cssNames";
|
import { cssNames } from "../../utils/cssNames";
|
||||||
import { TooltipDecoratorProps, withTooltip } from "../tooltip";
|
import { TooltipDecoratorProps, withTooltip } from "../tooltip";
|
||||||
|
|
||||||
interface Props extends React.HTMLAttributes<any>, TooltipDecoratorProps {
|
export interface BadgeProps extends React.HTMLAttributes<any>, TooltipDecoratorProps {
|
||||||
small?: boolean;
|
small?: boolean;
|
||||||
label?: React.ReactNode;
|
label?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@withTooltip
|
@withTooltip
|
||||||
export class Badge extends React.Component<Props> {
|
export class Badge extends React.Component<BadgeProps> {
|
||||||
render() {
|
render() {
|
||||||
const { className, label, small, children, ...elemProps } = this.props;
|
const { className, label, small, children, ...elemProps } = this.props;
|
||||||
return <>
|
return <>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import './checkbox.scss'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { autobind, cssNames } from "../../utils";
|
import { autobind, cssNames } from "../../utils";
|
||||||
|
|
||||||
interface Props<T = boolean> {
|
export interface CheckboxProps<T = boolean> {
|
||||||
theme?: "dark" | "light";
|
theme?: "dark" | "light";
|
||||||
className?: string;
|
className?: string;
|
||||||
label?: React.ReactNode;
|
label?: React.ReactNode;
|
||||||
@ -12,7 +12,7 @@ interface Props<T = boolean> {
|
|||||||
onChange?(value: T, evt: React.ChangeEvent<HTMLInputElement>): void;
|
onChange?(value: T, evt: React.ChangeEvent<HTMLInputElement>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Checkbox extends React.PureComponent<Props> {
|
export class Checkbox extends React.PureComponent<CheckboxProps> {
|
||||||
private input: HTMLInputElement;
|
private input: HTMLInputElement;
|
||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
|
|||||||
@ -9,7 +9,10 @@ import { Button, ButtonProps } from "../button";
|
|||||||
import { Dialog, DialogProps } from "../dialog";
|
import { Dialog, DialogProps } from "../dialog";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
|
|
||||||
export interface IConfirmDialogParams {
|
export interface ConfirmDialogProps extends Partial<DialogProps> {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfirmDialogParams {
|
||||||
ok?: () => void;
|
ok?: () => void;
|
||||||
labelOk?: ReactNode;
|
labelOk?: ReactNode;
|
||||||
labelCancel?: ReactNode;
|
labelCancel?: ReactNode;
|
||||||
@ -19,17 +22,14 @@ export interface IConfirmDialogParams {
|
|||||||
cancelButtonProps?: Partial<ButtonProps>
|
cancelButtonProps?: Partial<ButtonProps>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props extends Partial<DialogProps> {
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ConfirmDialog extends React.Component<Props> {
|
export class ConfirmDialog extends React.Component<ConfirmDialogProps> {
|
||||||
@observable static isOpen = false;
|
@observable static isOpen = false;
|
||||||
@observable.ref static params: IConfirmDialogParams;
|
@observable.ref static params: ConfirmDialogParams;
|
||||||
|
|
||||||
@observable isSaving = false;
|
@observable isSaving = false;
|
||||||
|
|
||||||
static open(params: IConfirmDialogParams) {
|
static open(params: ConfirmDialogParams) {
|
||||||
ConfirmDialog.isOpen = true;
|
ConfirmDialog.isOpen = true;
|
||||||
ConfirmDialog.params = params;
|
ConfirmDialog.params = params;
|
||||||
}
|
}
|
||||||
@ -38,14 +38,14 @@ export class ConfirmDialog extends React.Component<Props> {
|
|||||||
ConfirmDialog.isOpen = false;
|
ConfirmDialog.isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public defaultParams: IConfirmDialogParams = {
|
public defaultParams: ConfirmDialogParams = {
|
||||||
ok: noop,
|
ok: noop,
|
||||||
labelOk: <Trans>Ok</Trans>,
|
labelOk: <Trans>Ok</Trans>,
|
||||||
labelCancel: <Trans>Cancel</Trans>,
|
labelCancel: <Trans>Cancel</Trans>,
|
||||||
icon: <Icon big material="warning"/>,
|
icon: <Icon big material="warning"/>,
|
||||||
};
|
};
|
||||||
|
|
||||||
get params(): IConfirmDialogParams {
|
get params(): ConfirmDialogParams {
|
||||||
return Object.assign({}, this.defaultParams, ConfirmDialog.params);
|
return Object.assign({}, this.defaultParams, ConfirmDialog.params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import React from "react";
|
|||||||
import { DrawerItem, DrawerItemProps } from "./drawer-item";
|
import { DrawerItem, DrawerItemProps } from "./drawer-item";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
|
|
||||||
interface Props extends DrawerItemProps {
|
export interface DrawerItemLabelsProps extends DrawerItemProps {
|
||||||
labels: string[];
|
labels: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DrawerItemLabels(props: Props) {
|
export function DrawerItemLabels(props: DrawerItemLabelsProps) {
|
||||||
const { labels, ...itemProps } = props;
|
const { labels, ...itemProps } = props;
|
||||||
if (!labels || !labels.length) {
|
if (!labels || !labels.length) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -5,14 +5,14 @@ import { Icon } from "../icon";
|
|||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import { _i18n } from "../../i18n";
|
import { _i18n } from "../../i18n";
|
||||||
|
|
||||||
interface Props {
|
export interface DrawerParamTogglerProps {
|
||||||
label: string | number;
|
label: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
}
|
}
|
||||||
export class DrawerParamToggler extends React.Component<Props, State> {
|
export class DrawerParamToggler extends React.Component<DrawerParamTogglerProps, State> {
|
||||||
public state: State = {}
|
public state: State = {}
|
||||||
|
|
||||||
toggle = () => {
|
toggle = () => {
|
||||||
|
|||||||
@ -2,12 +2,12 @@ import "./drawer-title.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
|
|
||||||
interface Props {
|
export interface DrawerTitleProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
title?: React.ReactNode;
|
title?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DrawerTitle extends React.Component<Props> {
|
export class DrawerTitle extends React.Component<DrawerTitleProps> {
|
||||||
render() {
|
render() {
|
||||||
const { title, children, className } = this.props
|
const { title, children, className } = this.props
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -3,13 +3,16 @@ import "./input.scss";
|
|||||||
import React, { DOMAttributes, InputHTMLAttributes, TextareaHTMLAttributes } from "react";
|
import React, { DOMAttributes, InputHTMLAttributes, TextareaHTMLAttributes } from "react";
|
||||||
import { autobind, cssNames, debouncePromise } from "../../utils";
|
import { autobind, cssNames, debouncePromise } from "../../utils";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { conditionalValidators, Validator } from "./input_validators";
|
import * as Validators from "./input_validators";
|
||||||
|
import { InputValidator } from "./input_validators";
|
||||||
import isString from "lodash/isString"
|
import isString from "lodash/isString"
|
||||||
import isFunction from "lodash/isFunction"
|
import isFunction from "lodash/isFunction"
|
||||||
import isBoolean from "lodash/isBoolean"
|
import isBoolean from "lodash/isBoolean"
|
||||||
import uniqueId from "lodash/uniqueId"
|
import uniqueId from "lodash/uniqueId"
|
||||||
|
|
||||||
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
|
const { conditionalValidators, ...InputValidators } = Validators;
|
||||||
|
export { InputValidators, InputValidator }
|
||||||
|
|
||||||
type InputElement = HTMLInputElement | HTMLTextAreaElement;
|
type InputElement = HTMLInputElement | HTMLTextAreaElement;
|
||||||
type InputElementProps = InputHTMLAttributes<InputElement> & TextareaHTMLAttributes<InputElement> & DOMAttributes<InputElement>;
|
type InputElementProps = InputHTMLAttributes<InputElement> & TextareaHTMLAttributes<InputElement> & DOMAttributes<InputElement>;
|
||||||
|
|
||||||
@ -25,7 +28,7 @@ export type InputProps<T = string> = Omit<InputElementProps, "onChange" | "onSub
|
|||||||
iconLeft?: string | React.ReactNode; // material-icon name in case of string-type
|
iconLeft?: string | React.ReactNode; // material-icon name in case of string-type
|
||||||
iconRight?: string | React.ReactNode;
|
iconRight?: string | React.ReactNode;
|
||||||
contentRight?: string | React.ReactNode; // Any component of string goes after iconRight
|
contentRight?: string | React.ReactNode; // Any component of string goes after iconRight
|
||||||
validators?: Validator | Validator[];
|
validators?: InputValidator | InputValidator[];
|
||||||
onChange?(value: T, evt: React.ChangeEvent<InputElement>): void;
|
onChange?(value: T, evt: React.ChangeEvent<InputElement>): void;
|
||||||
onSubmit?(value: T): void;
|
onSubmit?(value: T): void;
|
||||||
}
|
}
|
||||||
@ -50,7 +53,7 @@ export class Input extends React.Component<InputProps, State> {
|
|||||||
static defaultProps = defaultProps as object;
|
static defaultProps = defaultProps as object;
|
||||||
|
|
||||||
public input: InputElement;
|
public input: InputElement;
|
||||||
public validators: Validator[] = [];
|
public validators: InputValidator[] = [];
|
||||||
|
|
||||||
public state: State = {
|
public state: State = {
|
||||||
dirty: !!this.props.dirty,
|
dirty: !!this.props.dirty,
|
||||||
@ -150,7 +153,7 @@ export class Input extends React.Component<InputProps, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getValidatorError(value: string, { message }: Validator) {
|
private getValidatorError(value: string, { message }: InputValidator) {
|
||||||
if (isFunction(message)) return message(value, this.props)
|
if (isFunction(message)) return message(value, this.props)
|
||||||
return message || "";
|
return message || "";
|
||||||
}
|
}
|
||||||
@ -293,7 +296,7 @@ export class Input extends React.Component<InputProps, State> {
|
|||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<label className="input-area flex gaps align-center">
|
<label className="input-area flex gaps align-center">
|
||||||
{isString(iconLeft) ? <Icon material={iconLeft} /> : iconLeft}
|
{isString(iconLeft) ? <Icon material={iconLeft}/> : iconLeft}
|
||||||
{multiLine ? <textarea {...inputProps as any} /> : <input {...inputProps as any} />}
|
{multiLine ? <textarea {...inputProps as any} /> : <input {...inputProps as any} />}
|
||||||
{isString(iconRight) ? <Icon material={iconRight} /> : iconRight}
|
{isString(iconRight) ? <Icon material={iconRight} /> : iconRight}
|
||||||
{contentRight}
|
{contentRight}
|
||||||
|
|||||||
@ -4,26 +4,26 @@ import { t } from "@lingui/macro";
|
|||||||
import { _i18n } from '../../i18n';
|
import { _i18n } from '../../i18n';
|
||||||
import fse from "fs-extra";
|
import fse from "fs-extra";
|
||||||
|
|
||||||
export interface Validator {
|
export interface InputValidator {
|
||||||
debounce?: number; // debounce for async validators in ms
|
debounce?: number; // debounce for async validators in ms
|
||||||
condition?(props: InputProps): boolean; // auto-bind condition depending on input props
|
condition?(props: InputProps): boolean; // auto-bind condition depending on input props
|
||||||
message?: ReactNode | ((value: string, props?: InputProps) => ReactNode | string);
|
message?: ReactNode | ((value: string, props?: InputProps) => ReactNode | string);
|
||||||
validate(value: string, props?: InputProps): boolean | Promise<any>; // promise can throw error message
|
validate(value: string, props?: InputProps): boolean | Promise<any>; // promise can throw error message
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isRequired: Validator = {
|
export const isRequired: InputValidator = {
|
||||||
condition: ({ required }) => required,
|
condition: ({ required }) => required,
|
||||||
message: () => _i18n._(t`This field is required`),
|
message: () => _i18n._(t`This field is required`),
|
||||||
validate: value => !!value.trim(),
|
validate: value => !!value.trim(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isEmail: Validator = {
|
export const isEmail: InputValidator = {
|
||||||
condition: ({ type }) => type === "email",
|
condition: ({ type }) => type === "email",
|
||||||
message: () => _i18n._(t`Wrong email format`),
|
message: () => _i18n._(t`Wrong email format`),
|
||||||
validate: value => !!value.match(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/),
|
validate: value => !!value.match(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isNumber: Validator = {
|
export const isNumber: InputValidator = {
|
||||||
condition: ({ type }) => type === "number",
|
condition: ({ type }) => type === "number",
|
||||||
message: () => _i18n._(t`Invalid number`),
|
message: () => _i18n._(t`Invalid number`),
|
||||||
validate: (value, { min, max }) => {
|
validate: (value, { min, max }) => {
|
||||||
@ -36,37 +36,37 @@ export const isNumber: Validator = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isUrl: Validator = {
|
export const isUrl: InputValidator = {
|
||||||
condition: ({ type }) => type === "url",
|
condition: ({ type }) => type === "url",
|
||||||
message: () => _i18n._(t`Wrong url format`),
|
message: () => _i18n._(t`Wrong url format`),
|
||||||
validate: value => !!value.match(/^$|^http(s)?:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)*$/),
|
validate: value => !!value.match(/^$|^http(s)?:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)*$/),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isPath: Validator = {
|
export const isPath: InputValidator = {
|
||||||
condition: ({ type }) => type === "text",
|
condition: ({ type }) => type === "text",
|
||||||
message: () => _i18n._(t`This field must be a valid path`),
|
message: () => _i18n._(t`This field must be a valid path`),
|
||||||
validate: value => !value || fse.pathExistsSync(value),
|
validate: value => !value || fse.pathExistsSync(value),
|
||||||
}
|
}
|
||||||
|
|
||||||
export const minLength: Validator = {
|
export const minLength: InputValidator = {
|
||||||
condition: ({ minLength }) => !!minLength,
|
condition: ({ minLength }) => !!minLength,
|
||||||
message: (value, { minLength }) => _i18n._(t`Minimum length is ${minLength}`),
|
message: (value, { minLength }) => _i18n._(t`Minimum length is ${minLength}`),
|
||||||
validate: (value, { minLength }) => value.length >= minLength,
|
validate: (value, { minLength }) => value.length >= minLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const maxLength: Validator = {
|
export const maxLength: InputValidator = {
|
||||||
condition: ({ maxLength }) => !!maxLength,
|
condition: ({ maxLength }) => !!maxLength,
|
||||||
message: (value, { maxLength }) => _i18n._(t`Maximum length is ${maxLength}`),
|
message: (value, { maxLength }) => _i18n._(t`Maximum length is ${maxLength}`),
|
||||||
validate: (value, { maxLength }) => value.length <= maxLength,
|
validate: (value, { maxLength }) => value.length <= maxLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
const systemNameMatcher = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/;
|
const systemNameMatcher = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/;
|
||||||
export const systemName: Validator = {
|
export const systemName: InputValidator = {
|
||||||
message: () => _i18n._(t`A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics.`),
|
message: () => _i18n._(t`A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics.`),
|
||||||
validate: value => !!value.match(systemNameMatcher),
|
validate: value => !!value.match(systemNameMatcher),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const accountId: Validator = {
|
export const accountId: InputValidator = {
|
||||||
message: () => _i18n._(t`Invalid account ID`),
|
message: () => _i18n._(t`Invalid account ID`),
|
||||||
validate: value => (isEmail.validate(value) || systemName.validate(value))
|
validate: value => (isEmail.validate(value) || systemName.validate(value))
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import React, { ReactNode } from "react";
|
|||||||
import { computed, observable, reaction, toJS, when } from "mobx";
|
import { computed, observable, reaction, toJS, when } from "mobx";
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { Plural, Trans } from "@lingui/macro";
|
import { Plural, Trans } from "@lingui/macro";
|
||||||
import { ConfirmDialog, IConfirmDialogParams } from "../confirm-dialog";
|
import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog";
|
||||||
import { SortingCallback, Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps } from "../table";
|
import { TableSortCallback, Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps } from "../table";
|
||||||
import { autobind, createStorage, cssNames, IClassName, isReactNode, noop, prevDefault, stopPropagation } from "../../utils";
|
import { autobind, createStorage, cssNames, IClassName, isReactNode, noop, prevDefault, stopPropagation } from "../../utils";
|
||||||
import { AddRemoveButtons, AddRemoveButtonsProps } from "../add-remove-buttons";
|
import { AddRemoveButtons, AddRemoveButtonsProps } from "../add-remove-buttons";
|
||||||
import { NoItems } from "../no-items";
|
import { NoItems } from "../no-items";
|
||||||
@ -52,7 +52,7 @@ export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
|||||||
isSelectable?: boolean; // show checkbox in rows for selecting items
|
isSelectable?: boolean; // show checkbox in rows for selecting items
|
||||||
isSearchable?: boolean; // apply search-filter & add search-input
|
isSearchable?: boolean; // apply search-filter & add search-input
|
||||||
copyClassNameFromHeadCells?: boolean;
|
copyClassNameFromHeadCells?: boolean;
|
||||||
sortingCallbacks?: { [sortBy: string]: SortingCallback };
|
sortingCallbacks?: { [sortBy: string]: TableSortCallback };
|
||||||
tableProps?: Partial<TableProps>; // low-level table configuration
|
tableProps?: Partial<TableProps>; // low-level table configuration
|
||||||
renderTableHeader: TableCellProps[] | null;
|
renderTableHeader: TableCellProps[] | null;
|
||||||
renderTableContents: (item: T) => (ReactNode | TableCellProps)[];
|
renderTableContents: (item: T) => (ReactNode | TableCellProps)[];
|
||||||
@ -67,7 +67,7 @@ export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
|||||||
onDetails?: (item: T) => void;
|
onDetails?: (item: T) => void;
|
||||||
|
|
||||||
// other
|
// other
|
||||||
customizeRemoveDialog?: (selectedItems: T[]) => Partial<IConfirmDialogParams>;
|
customizeRemoveDialog?: (selectedItems: T[]) => Partial<ConfirmDialogParams>;
|
||||||
renderFooter?: (parent: ItemListLayout) => React.ReactNode;
|
renderFooter?: (parent: ItemListLayout) => React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
export * from "./kube-object-details"
|
export * from "./kube-object-details"
|
||||||
export * from "./kube-object-list-layout"
|
export * from "./kube-object-list-layout"
|
||||||
export * from "./kube-object-menu"
|
export * from "./kube-object-menu"
|
||||||
|
export * from "./kube-object-meta"
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { Spinner } from "../spinner";
|
|||||||
import { apiManager } from "../../api/api-manager";
|
import { apiManager } from "../../api/api-manager";
|
||||||
import { crdStore } from "../+custom-resources/crd.store";
|
import { crdStore } from "../+custom-resources/crd.store";
|
||||||
import { CrdResourceDetails } from "../+custom-resources";
|
import { CrdResourceDetails } from "../+custom-resources";
|
||||||
import { KubeObjectMenu } from "./kube-object-menu"
|
import { KubeObjectMenu } from "./kube-object-menu"
|
||||||
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
|
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
|
||||||
|
|
||||||
export interface KubeObjectDetailsProps<T = KubeObject> {
|
export interface KubeObjectDetailsProps<T = KubeObject> {
|
||||||
|
|||||||
@ -2,17 +2,16 @@ import React from "react";
|
|||||||
import { Trans } from "@lingui/macro";
|
import { Trans } from "@lingui/macro";
|
||||||
import { IKubeMetaField, KubeObject } from "../../api/kube-object";
|
import { IKubeMetaField, KubeObject } from "../../api/kube-object";
|
||||||
import { DrawerItem, DrawerItemLabels } from "../drawer";
|
import { DrawerItem, DrawerItemLabels } from "../drawer";
|
||||||
import { WorkloadKubeObject } from "../../api/workload-kube-object";
|
|
||||||
import { getDetailsUrl } from "../../navigation";
|
import { getDetailsUrl } from "../../navigation";
|
||||||
import { lookupApiLink } from "../../api/kube-api";
|
import { lookupApiLink } from "../../api/kube-api";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
export interface Props {
|
export interface KubeObjectMetaProps {
|
||||||
object: KubeObject;
|
object: KubeObject;
|
||||||
hideFields?: IKubeMetaField[];
|
hideFields?: IKubeMetaField[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KubeObjectMeta extends React.Component<Props> {
|
export class KubeObjectMeta extends React.Component<KubeObjectMetaProps> {
|
||||||
static defaultHiddenFields: IKubeMetaField[] = [
|
static defaultHiddenFields: IKubeMetaField[] = [
|
||||||
"uid", "resourceVersion", "selfLink"
|
"uid", "resourceVersion", "selfLink"
|
||||||
];
|
];
|
||||||
|
|||||||
@ -12,14 +12,14 @@ export interface TabRoute extends RouteProps {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
export interface TabLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
className?: any;
|
className?: any;
|
||||||
tabs?: TabRoute[];
|
tabs?: TabRoute[];
|
||||||
contentClass?: string;
|
contentClass?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TabLayout = observer(({ className, contentClass, tabs, children }: Props) => {
|
export const TabLayout = observer(({ className, contentClass, tabs, children }: TabLayoutProps) => {
|
||||||
const routePath = navigation.location.pathname;
|
const routePath = navigation.location.pathname;
|
||||||
return (
|
return (
|
||||||
<div className={cssNames("TabLayout", className)}>
|
<div className={cssNames("TabLayout", className)}>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { cssNames, IClassName } from "../../utils";
|
import { cssNames, IClassName } from "../../utils";
|
||||||
|
|
||||||
interface Props extends React.DOMAttributes<any> {
|
export interface WizardLayoutProps extends React.DOMAttributes<any> {
|
||||||
className?: IClassName;
|
className?: IClassName;
|
||||||
header?: React.ReactNode;
|
header?: React.ReactNode;
|
||||||
headerClass?: IClassName;
|
headerClass?: IClassName;
|
||||||
@ -15,7 +15,7 @@ interface Props extends React.DOMAttributes<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class WizardLayout extends React.Component<Props> {
|
export class WizardLayout extends React.Component<WizardLayoutProps> {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
className, contentClass, infoPanelClass, infoPanel, header, headerClass, centered,
|
className, contentClass, infoPanelClass, infoPanel, header, headerClass, centered,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React from "react";
|
|||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import { TooltipDecoratorProps, withTooltip } from "../tooltip";
|
import { TooltipDecoratorProps, withTooltip } from "../tooltip";
|
||||||
|
|
||||||
interface Props extends React.HTMLProps<any>, TooltipDecoratorProps {
|
export interface LineProgressProps extends React.HTMLProps<any>, TooltipDecoratorProps {
|
||||||
value: number;
|
value: number;
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
@ -12,8 +12,8 @@ interface Props extends React.HTMLProps<any>, TooltipDecoratorProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@withTooltip
|
@withTooltip
|
||||||
export class LineProgress extends React.PureComponent<Props> {
|
export class LineProgress extends React.PureComponent<LineProgressProps> {
|
||||||
static defaultProps: Props = {
|
static defaultProps: LineProgressProps = {
|
||||||
value: 0,
|
value: 0,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
max: 100,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ 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;
|
||||||
|
|
||||||
interface MenuPosition {
|
export interface MenuPosition {
|
||||||
left?: boolean;
|
left?: boolean;
|
||||||
top?: boolean;
|
top?: boolean;
|
||||||
right?: boolean;
|
right?: boolean;
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export * from './notifications'
|
export * from './notifications'
|
||||||
|
export * from './notifications.store'
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import isObject from "lodash/isObject"
|
|||||||
import uniqueId from "lodash/uniqueId";
|
import uniqueId from "lodash/uniqueId";
|
||||||
import { JsonApiErrorParsed } from "../../api/json-api";
|
import { JsonApiErrorParsed } from "../../api/json-api";
|
||||||
|
|
||||||
export type IMessageId = string | number;
|
export type NotificationId = string | number;
|
||||||
export type IMessage = React.ReactNode | React.ReactNode[] | JsonApiErrorParsed;
|
export type NotificationMessage = React.ReactNode | React.ReactNode[] | JsonApiErrorParsed;
|
||||||
|
|
||||||
export enum NotificationStatus {
|
export enum NotificationStatus {
|
||||||
OK = "ok",
|
OK = "ok",
|
||||||
@ -14,20 +14,20 @@ export enum NotificationStatus {
|
|||||||
INFO = "info",
|
INFO = "info",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INotification {
|
export interface Notification {
|
||||||
id?: IMessageId;
|
id?: NotificationId;
|
||||||
message: IMessage;
|
message: NotificationMessage;
|
||||||
status?: NotificationStatus;
|
status?: NotificationStatus;
|
||||||
timeout?: number; // auto-hiding timeout in milliseconds, 0 = no hide
|
timeout?: number; // auto-hiding timeout in milliseconds, 0 = no hide
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
export class NotificationsStore {
|
export class NotificationsStore {
|
||||||
public notifications = observable<INotification>([], { deep: false });
|
public notifications = observable<Notification>([], { deep: false });
|
||||||
|
|
||||||
protected autoHideTimers = new Map<IMessageId, number>();
|
protected autoHideTimers = new Map<NotificationId, number>();
|
||||||
|
|
||||||
addAutoHideTimer(notification: INotification) {
|
addAutoHideTimer(notification: Notification) {
|
||||||
this.removeAutoHideTimer(notification);
|
this.removeAutoHideTimer(notification);
|
||||||
const { id, timeout } = notification;
|
const { id, timeout } = notification;
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
@ -36,7 +36,7 @@ export class NotificationsStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAutoHideTimer(notification: INotification) {
|
removeAutoHideTimer(notification: Notification) {
|
||||||
const { id } = notification;
|
const { id } = notification;
|
||||||
if (this.autoHideTimers.has(id)) {
|
if (this.autoHideTimers.has(id)) {
|
||||||
clearTimeout(this.autoHideTimers.get(id));
|
clearTimeout(this.autoHideTimers.get(id));
|
||||||
@ -45,7 +45,7 @@ export class NotificationsStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
add(notification: INotification) {
|
add(notification: Notification) {
|
||||||
if (!notification.id) {
|
if (!notification.id) {
|
||||||
notification.id = uniqueId("notification_");
|
notification.id = uniqueId("notification_");
|
||||||
}
|
}
|
||||||
@ -56,11 +56,11 @@ export class NotificationsStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
remove(itemOrId: IMessageId | INotification) {
|
remove(itemOrId: NotificationId | Notification) {
|
||||||
if (!isObject(itemOrId)) {
|
if (!isObject(itemOrId)) {
|
||||||
itemOrId = this.notifications.find(item => item.id === itemOrId);
|
itemOrId = this.notifications.find(item => item.id === itemOrId);
|
||||||
}
|
}
|
||||||
return this.notifications.remove(itemOrId as INotification);
|
return this.notifications.remove(itemOrId as Notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { reaction } from "mobx";
|
|||||||
import { disposeOnUnmount, observer } from "mobx-react"
|
import { disposeOnUnmount, observer } from "mobx-react"
|
||||||
import { JsonApiErrorParsed } from "../../api/json-api";
|
import { JsonApiErrorParsed } from "../../api/json-api";
|
||||||
import { cssNames, prevDefault } from "../../utils";
|
import { cssNames, prevDefault } from "../../utils";
|
||||||
import { IMessage, INotification, notificationsStore, NotificationStatus } from "./notifications.store";
|
import { NotificationMessage, Notification, notificationsStore, NotificationStatus } from "./notifications.store";
|
||||||
import { Animate } from "../animate";
|
import { Animate } from "../animate";
|
||||||
import { Icon } from "../icon"
|
import { Icon } from "../icon"
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ import { Icon } from "../icon"
|
|||||||
export class Notifications extends React.Component {
|
export class Notifications extends React.Component {
|
||||||
public elem: HTMLElement;
|
public elem: HTMLElement;
|
||||||
|
|
||||||
static ok(message: IMessage) {
|
static ok(message: NotificationMessage) {
|
||||||
notificationsStore.add({
|
notificationsStore.add({
|
||||||
message: message,
|
message: message,
|
||||||
timeout: 2500,
|
timeout: 2500,
|
||||||
@ -21,7 +21,7 @@ export class Notifications extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static error(message: IMessage) {
|
static error(message: NotificationMessage) {
|
||||||
notificationsStore.add({
|
notificationsStore.add({
|
||||||
message: message,
|
message: message,
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
@ -29,7 +29,7 @@ export class Notifications extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static info(message: IMessage, customOpts: Partial<INotification> = {}) {
|
static info(message: NotificationMessage, customOpts: Partial<Notification> = {}) {
|
||||||
return notificationsStore.add({
|
return notificationsStore.add({
|
||||||
status: NotificationStatus.INFO,
|
status: NotificationStatus.INFO,
|
||||||
timeout: 0,
|
timeout: 0,
|
||||||
@ -56,7 +56,7 @@ export class Notifications extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getMessage(notification: INotification) {
|
getMessage(notification: Notification) {
|
||||||
let { message } = notification;
|
let { message } = notification;
|
||||||
if (message instanceof JsonApiErrorParsed) {
|
if (message instanceof JsonApiErrorParsed) {
|
||||||
message = message.toString();
|
message = message.toString();
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import uniqueId from "lodash/uniqueId";
|
|||||||
|
|
||||||
// todo: refactor with contexts
|
// todo: refactor with contexts
|
||||||
|
|
||||||
interface RadioGroupProps {
|
export interface RadioGroupProps {
|
||||||
className?: any;
|
className?: any;
|
||||||
value?: any;
|
value?: any;
|
||||||
asButtons?: boolean;
|
asButtons?: boolean;
|
||||||
@ -35,7 +35,7 @@ export class RadioGroup extends React.Component<RadioGroupProps, {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RadioProps = React.HTMLProps<any> & {
|
export type RadioProps = React.HTMLProps<any> & {
|
||||||
name?: string;
|
name?: string;
|
||||||
label?: React.ReactNode | any;
|
label?: React.ReactNode | any;
|
||||||
value?: any;
|
value?: any;
|
||||||
|
|||||||
@ -4,20 +4,20 @@ import "./slider.scss";
|
|||||||
|
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import MaterialSlider, { SliderClassKey, SliderProps } from "@material-ui/core/Slider";
|
import MaterialSlider, { SliderClassKey, SliderProps as MaterialSliderProps } from "@material-ui/core/Slider";
|
||||||
|
|
||||||
interface Props extends Omit<SliderProps, "onChange"> {
|
export interface SliderProps extends Omit<MaterialSliderProps, "onChange"> {
|
||||||
className?: string;
|
className?: string;
|
||||||
onChange?(evt: React.FormEvent<any>, value: number): void;
|
onChange?(evt: React.FormEvent<any>, value: number): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps: Partial<Props> = {
|
const defaultProps: Partial<SliderProps> = {
|
||||||
step: 1,
|
step: 1,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
max: 100,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Slider extends Component<Props> {
|
export class Slider extends Component<SliderProps> {
|
||||||
static defaultProps = defaultProps as object;
|
static defaultProps = defaultProps as object;
|
||||||
|
|
||||||
private classNames: Partial<{ [P in SliderClassKey]: string }> = {
|
private classNames: Partial<{ [P in SliderClassKey]: string }> = {
|
||||||
|
|||||||
@ -2,12 +2,12 @@ import './cube-spinner.scss'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
|
|
||||||
interface Props {
|
export interface CubeSpinnerProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
center?: boolean;
|
center?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CubeSpinner extends React.Component<Props> {
|
export class CubeSpinner extends React.Component<CubeSpinnerProps> {
|
||||||
render() {
|
render() {
|
||||||
const { className, center } = this.props;
|
const { className, center } = this.props;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -3,12 +3,12 @@ import './spinner.scss'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
|
|
||||||
interface Props extends React.HTMLProps<any> {
|
export interface SpinnerProps extends React.HTMLProps<any> {
|
||||||
singleColor?: boolean;
|
singleColor?: boolean;
|
||||||
center?: boolean;
|
center?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Spinner extends React.Component<Props, {}> {
|
export class Spinner extends React.Component<SpinnerProps, {}> {
|
||||||
private elem: HTMLElement;
|
private elem: HTMLElement;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
|||||||
@ -4,11 +4,11 @@ import React from "react";
|
|||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
import { TooltipDecoratorProps, withTooltip } from "../tooltip";
|
import { TooltipDecoratorProps, withTooltip } from "../tooltip";
|
||||||
|
|
||||||
interface Props extends React.HTMLAttributes<any>, TooltipDecoratorProps {
|
export interface StatusBrickProps extends React.HTMLAttributes<any>, TooltipDecoratorProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
@withTooltip
|
@withTooltip
|
||||||
export class StatusBrick extends React.Component<Props> {
|
export class StatusBrick extends React.Component<StatusBrickProps> {
|
||||||
render() {
|
render() {
|
||||||
const { className, ...elemProps } = this.props
|
const { className, ...elemProps } = this.props
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import "./stepper.scss";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { cssNames } from "../../utils";
|
import { cssNames } from "../../utils";
|
||||||
|
|
||||||
interface Props extends React.HTMLProps<any> {
|
export interface StepperProps extends React.HTMLProps<any> {
|
||||||
step: number;
|
step: number;
|
||||||
steps: Step[];
|
steps: Step[];
|
||||||
}
|
}
|
||||||
@ -11,7 +11,7 @@ interface Step {
|
|||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Stepper extends React.Component<Props, {}> {
|
export class Stepper extends React.Component<StepperProps, {}> {
|
||||||
render() {
|
render() {
|
||||||
const { className, steps, ...props } = this.props;
|
const { className, steps, ...props } = this.props;
|
||||||
const stepsCount = steps.length;
|
const stepsCount = steps.length;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import "./table-cell.scss";
|
import "./table-cell.scss";
|
||||||
import type { SortBy, SortParams } from "./table";
|
import type { TableSortBy, TableSortParams } from "./table";
|
||||||
|
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { autobind, cssNames } from "../../utils";
|
import { autobind, cssNames } from "../../utils";
|
||||||
@ -13,9 +13,9 @@ export interface TableCellProps extends React.DOMAttributes<HTMLDivElement> {
|
|||||||
title?: ReactNode;
|
title?: ReactNode;
|
||||||
checkbox?: boolean; // render cell with a checkbox
|
checkbox?: boolean; // render cell with a checkbox
|
||||||
isChecked?: boolean; // mark checkbox as checked or not
|
isChecked?: boolean; // mark checkbox as checked or not
|
||||||
sortBy?: SortBy; // column name, must be same as key in sortable object <Table sortable={}/>
|
sortBy?: TableSortBy; // column name, must be same as key in sortable object <Table sortable={}/>
|
||||||
_sorting?: Partial<SortParams>; // <Table> sorting state, don't use this prop outside (!)
|
_sorting?: Partial<TableSortParams>; // <Table> sorting state, don't use this prop outside (!)
|
||||||
_sort?(sortBy: SortBy): void; // <Table> sort function, don't use this prop outside (!)
|
_sort?(sortBy: TableSortBy): void; // <Table> sort function, don't use this prop outside (!)
|
||||||
_nowrap?: boolean; // indicator, might come from parent <TableHead>, don't use this prop outside (!)
|
_nowrap?: boolean; // indicator, might come from parent <TableHead>, don't use this prop outside (!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,10 +14,10 @@ import { ItemObject } from "../../item.store";
|
|||||||
|
|
||||||
// todo: refactor + decouple search from location
|
// todo: refactor + decouple search from location
|
||||||
|
|
||||||
export type SortBy = string;
|
export type TableSortBy = string;
|
||||||
export type OrderBy = "asc" | "desc" | string;
|
export type TableOrderBy = "asc" | "desc" | string;
|
||||||
export type SortParams = { sortBy: SortBy; orderBy: OrderBy }
|
export type TableSortParams = { sortBy: TableSortBy; orderBy: TableOrderBy }
|
||||||
export type SortingCallback<D = any> = (data: D) => string | number | (string | number)[];
|
export type TableSortCallback<D = any> = (data: D) => string | number | (string | number)[];
|
||||||
|
|
||||||
export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
|
export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
|
||||||
items?: ItemObject[]; // Raw items data
|
items?: ItemObject[]; // Raw items data
|
||||||
@ -29,11 +29,11 @@ export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
|
|||||||
sortable?: {
|
sortable?: {
|
||||||
// Define sortable callbacks for every column in <TableHead><TableCell sortBy="someCol"><TableHead>
|
// Define sortable callbacks for every column in <TableHead><TableCell sortBy="someCol"><TableHead>
|
||||||
// @sortItem argument in the callback is an object, provided in <TableRow sortItem={someColDataItem}/>
|
// @sortItem argument in the callback is an object, provided in <TableRow sortItem={someColDataItem}/>
|
||||||
[sortBy: string]: SortingCallback;
|
[sortBy: string]: TableSortCallback;
|
||||||
};
|
};
|
||||||
sortSyncWithUrl?: boolean; // sorting state is managed globally from url params
|
sortSyncWithUrl?: boolean; // sorting state is managed globally from url params
|
||||||
sortByDefault?: Partial<SortParams>; // default sorting params
|
sortByDefault?: Partial<TableSortParams>; // default sorting params
|
||||||
onSort?: (params: SortParams) => void; // callback on sort change, default: global sync with url
|
onSort?: (params: TableSortParams) => void; // callback on sort change, default: global sync with url
|
||||||
noItems?: React.ReactNode; // Show no items state table list is empty
|
noItems?: React.ReactNode; // Show no items state table list is empty
|
||||||
selectedItemId?: string; // Allows to scroll list to selected item
|
selectedItemId?: string; // Allows to scroll list to selected item
|
||||||
virtual?: boolean; // Use virtual list component to render only visible rows
|
virtual?: boolean; // Use virtual list component to render only visible rows
|
||||||
@ -55,7 +55,7 @@ export class Table extends React.Component<TableProps> {
|
|||||||
|
|
||||||
@observable sortParamsLocal = this.props.sortByDefault;
|
@observable sortParamsLocal = this.props.sortByDefault;
|
||||||
|
|
||||||
@computed get sortParams(): Partial<SortParams> {
|
@computed get sortParams(): Partial<TableSortParams> {
|
||||||
if (this.props.sortSyncWithUrl) {
|
if (this.props.sortSyncWithUrl) {
|
||||||
const sortBy = navigation.searchParams.get("sortBy")
|
const sortBy = navigation.searchParams.get("sortBy")
|
||||||
const orderBy = navigation.searchParams.get("orderBy")
|
const orderBy = navigation.searchParams.get("orderBy")
|
||||||
@ -105,7 +105,7 @@ export class Table extends React.Component<TableProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
protected onSort(params: SortParams) {
|
protected onSort(params: TableSortParams) {
|
||||||
const { sortSyncWithUrl, onSort } = this.props;
|
const { sortSyncWithUrl, onSort } = this.props;
|
||||||
if (sortSyncWithUrl) {
|
if (sortSyncWithUrl) {
|
||||||
setQueryParams(params)
|
setQueryParams(params)
|
||||||
@ -119,11 +119,11 @@ export class Table extends React.Component<TableProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
sort(colName: SortBy) {
|
sort(colName: TableSortBy) {
|
||||||
const { sortBy, orderBy } = this.sortParams;
|
const { sortBy, orderBy } = this.sortParams;
|
||||||
const sameColumn = sortBy == colName;
|
const sameColumn = sortBy == colName;
|
||||||
const newSortBy: SortBy = colName;
|
const newSortBy: TableSortBy = colName;
|
||||||
const newOrderBy: OrderBy = (!orderBy || !sameColumn || orderBy === "desc") ? "asc" : "desc";
|
const newOrderBy: TableOrderBy = (!orderBy || !sameColumn || orderBy === "desc") ? "asc" : "desc";
|
||||||
this.onSort({
|
this.onSort({
|
||||||
sortBy: String(newSortBy),
|
sortBy: String(newSortBy),
|
||||||
orderBy: newOrderBy,
|
orderBy: newOrderBy,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"
|
|||||||
import { isDevelopment, isProduction, mainDir, buildDir } from "./src/common/vars";
|
import { isDevelopment, isProduction, mainDir, buildDir } from "./src/common/vars";
|
||||||
import nodeExternals from "webpack-node-externals";
|
import nodeExternals from "webpack-node-externals";
|
||||||
import ProgressBarPlugin from "progress-bar-webpack-plugin";
|
import ProgressBarPlugin from "progress-bar-webpack-plugin";
|
||||||
|
import HardSourceWebpackPlugin from 'hard-source-webpack-plugin';
|
||||||
|
|
||||||
export default function (): webpack.Configuration {
|
export default function (): webpack.Configuration {
|
||||||
console.info('WEBPACK:main', require("./src/common/vars"))
|
console.info('WEBPACK:main', require("./src/common/vars"))
|
||||||
@ -47,6 +48,7 @@ export default function (): webpack.Configuration {
|
|||||||
plugins: [
|
plugins: [
|
||||||
new ProgressBarPlugin(),
|
new ProgressBarPlugin(),
|
||||||
new ForkTsCheckerPlugin(),
|
new ForkTsCheckerPlugin(),
|
||||||
]
|
isDevelopment && new HardSourceWebpackPlugin(),
|
||||||
|
].filter(Boolean)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { appName, buildDir, htmlTemplate, isDevelopment, isProduction, publicPath, rendererDir, sassCommonVars } from "./src/common/vars";
|
import { appName, buildDir, htmlTemplate, isDevelopment, isProduction, publicPath, rendererDir, sassCommonVars, webpackDevServerPort } from "./src/common/vars";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import webpack from "webpack";
|
import webpack from "webpack";
|
||||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||||
@ -6,6 +6,8 @@ import MiniCssExtractPlugin from "mini-css-extract-plugin";
|
|||||||
import TerserPlugin from "terser-webpack-plugin";
|
import TerserPlugin from "terser-webpack-plugin";
|
||||||
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"
|
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"
|
||||||
import ProgressBarPlugin from "progress-bar-webpack-plugin";
|
import ProgressBarPlugin from "progress-bar-webpack-plugin";
|
||||||
|
import HardSourceWebpackPlugin from 'hard-source-webpack-plugin';
|
||||||
|
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
webpackLensRenderer
|
webpackLensRenderer
|
||||||
@ -19,6 +21,15 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
|
|||||||
context: __dirname,
|
context: __dirname,
|
||||||
target: "electron-renderer",
|
target: "electron-renderer",
|
||||||
devtool: "source-map", // todo: optimize in dev-mode with webpack.SourceMapDevToolPlugin
|
devtool: "source-map", // todo: optimize in dev-mode with webpack.SourceMapDevToolPlugin
|
||||||
|
devServer: {
|
||||||
|
contentBase: buildDir,
|
||||||
|
port: webpackDevServerPort,
|
||||||
|
host: "localhost",
|
||||||
|
hot: true,
|
||||||
|
// to avoid cors errors when requests is from iframes
|
||||||
|
disableHostCheck: true,
|
||||||
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
||||||
|
},
|
||||||
name: "lens-app",
|
name: "lens-app",
|
||||||
mode: isProduction ? "production" : "development",
|
mode: isProduction ? "production" : "development",
|
||||||
cache: isDevelopment,
|
cache: isDevelopment,
|
||||||
@ -79,7 +90,10 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
|
|||||||
["@babel/preset-env", {
|
["@babel/preset-env", {
|
||||||
modules: "commonjs" // ling-ui
|
modules: "commonjs" // ling-ui
|
||||||
}],
|
}],
|
||||||
]
|
],
|
||||||
|
plugins: [
|
||||||
|
isDevelopment && require.resolve('react-refresh/babel'),
|
||||||
|
].filter(Boolean),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -172,6 +186,11 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
|
|||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: "[name].css",
|
filename: "[name].css",
|
||||||
}),
|
}),
|
||||||
],
|
|
||||||
|
isDevelopment && new HardSourceWebpackPlugin(),
|
||||||
|
isDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||||
|
isDevelopment && new ReactRefreshWebpackPlugin(),
|
||||||
|
|
||||||
|
].filter(Boolean),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user