mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into set-kubectl-binary-path
This commit is contained in:
commit
51199154e8
4
Makefile
4
Makefile
@ -6,7 +6,7 @@ endif
|
|||||||
|
|
||||||
.PHONY: init dev build test clean
|
.PHONY: init dev build test clean
|
||||||
|
|
||||||
init: download-bins install-deps compile-dev
|
init: install-deps download-bins compile-dev
|
||||||
echo "Init done"
|
echo "Init done"
|
||||||
|
|
||||||
download-bins:
|
download-bins:
|
||||||
@ -46,7 +46,7 @@ integration-win:
|
|||||||
test-app:
|
test-app:
|
||||||
yarn test
|
yarn test
|
||||||
|
|
||||||
build: install-deps
|
build: install-deps download-bins
|
||||||
yarn install
|
yarn install
|
||||||
ifeq "$(DETECTED_OS)" "Windows"
|
ifeq "$(DETECTED_OS)" "Windows"
|
||||||
yarn dist:win
|
yarn dist:win
|
||||||
|
|||||||
10
README.md
10
README.md
@ -10,9 +10,9 @@ Lens is the only IDE you’ll ever need to take control of your Kubernetes clust
|
|||||||
|
|
||||||
## What makes Lens special?
|
## What makes Lens special?
|
||||||
|
|
||||||
* Amazing usability and end user experience
|
* Amazing usability and end-user experience
|
||||||
* Multi cluster management; Support for hundreds of clusters
|
* Multi cluster management: support for hundreds of clusters
|
||||||
* Standalone application; No need to install anything in-cluster
|
* Standalone application: no need to install anything in-cluster
|
||||||
* Real-time cluster state visualization
|
* Real-time cluster state visualization
|
||||||
* Resource utilization charts and trends with history powered by built-in Prometheus
|
* Resource utilization charts and trends with history powered by built-in Prometheus
|
||||||
* Terminal access to nodes and containers
|
* Terminal access to nodes and containers
|
||||||
@ -38,7 +38,7 @@ brew cask install lens
|
|||||||
|
|
||||||
## Development (advanced)
|
## Development (advanced)
|
||||||
|
|
||||||
Allows faster separately re-run some of involved processes:
|
Allows for faster separate re-runs of some of the more involved processes:
|
||||||
|
|
||||||
1. `yarn dev:main` compiles electron's main process part and start watching files
|
1. `yarn dev:main` compiles electron's main process part and start watching files
|
||||||
1. `yarn dev:renderer` compiles electron's renderer part and start watching files
|
1. `yarn dev:renderer` compiles electron's renderer part and start watching files
|
||||||
@ -54,4 +54,4 @@ Allows faster separately re-run some of involved processes:
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Bug reports and pull requests are welcome on GitHub at https://github.com/lensapp/lens.
|
Bug reports and pull requests are welcome on GitHub at https://github.com/lensapp/lens.
|
||||||
@ -11,7 +11,7 @@ const fileOptions: winston.transports.FileTransportOptions = {
|
|||||||
handleExceptions: false,
|
handleExceptions: false,
|
||||||
level: isDebugging ? "debug" : "info",
|
level: isDebugging ? "debug" : "info",
|
||||||
filename: "lens.log",
|
filename: "lens.log",
|
||||||
dirname: (app || remote.app).getPath("logs"),
|
dirname: (app ?? remote?.app)?.getPath("logs"),
|
||||||
maxsize: 16 * 1024,
|
maxsize: 16 * 1024,
|
||||||
maxFiles: 16,
|
maxFiles: 16,
|
||||||
tailable: true,
|
tailable: true,
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export function buildMenu(windowManager: WindowManager) {
|
|||||||
`${appName}: ${app.getVersion()}`,
|
`${appName}: ${app.getVersion()}`,
|
||||||
`Electron: ${process.versions.electron}`,
|
`Electron: ${process.versions.electron}`,
|
||||||
`Chrome: ${process.versions.chrome}`,
|
`Chrome: ${process.versions.chrome}`,
|
||||||
`Copyright 2020 Copyright 2020 Mirantis, Inc.`,
|
`Copyright 2020 Mirantis, Inc.`,
|
||||||
]
|
]
|
||||||
dialog.showMessageBoxSync(browserWindow, {
|
dialog.showMessageBoxSync(browserWindow, {
|
||||||
title: `${isWindows ? " ".repeat(2) : ""}${appName}`,
|
title: `${isWindows ? " ".repeat(2) : ""}${appName}`,
|
||||||
|
|||||||
@ -1,13 +1,28 @@
|
|||||||
import { KubeObject } from "../kube-object";
|
import { KubeObject } from "../kube-object";
|
||||||
import { KubeApi } from "../kube-api";
|
import { VersionedKubeApi } from "../kube-api-versioned";
|
||||||
import { crdResourcesURL } from "../../components/+custom-resources/crd.route";
|
import { crdResourcesURL } from "../../components/+custom-resources/crd.route";
|
||||||
|
|
||||||
|
type AdditionalPrinterColumnsCommon = {
|
||||||
|
name: string;
|
||||||
|
type: "integer" | "number" | "string" | "boolean" | "date";
|
||||||
|
priority: number;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdditionalPrinterColumnsV1 = AdditionalPrinterColumnsCommon & {
|
||||||
|
jsonPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdditionalPrinterColumnsV1Beta = AdditionalPrinterColumnsCommon & {
|
||||||
|
JSONPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class CustomResourceDefinition extends KubeObject {
|
export class CustomResourceDefinition extends KubeObject {
|
||||||
static kind = "CustomResourceDefinition";
|
static kind = "CustomResourceDefinition";
|
||||||
|
|
||||||
spec: {
|
spec: {
|
||||||
group: string;
|
group: string;
|
||||||
version: string;
|
version?: string; // deprecated in v1 api
|
||||||
names: {
|
names: {
|
||||||
plural: string;
|
plural: string;
|
||||||
singular: string;
|
singular: string;
|
||||||
@ -20,18 +35,14 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
name: string;
|
name: string;
|
||||||
served: boolean;
|
served: boolean;
|
||||||
storage: boolean;
|
storage: boolean;
|
||||||
|
schema?: unknown; // required in v1 but not present in v1beta
|
||||||
|
additionalPrinterColumns?: AdditionalPrinterColumnsV1[]
|
||||||
}[];
|
}[];
|
||||||
conversion: {
|
conversion: {
|
||||||
strategy?: string;
|
strategy?: string;
|
||||||
webhook?: any;
|
webhook?: any;
|
||||||
};
|
};
|
||||||
additionalPrinterColumns?: {
|
additionalPrinterColumns?: AdditionalPrinterColumnsV1Beta[]; // removed in v1
|
||||||
name: string;
|
|
||||||
type: "integer" | "number" | "string" | "boolean" | "date";
|
|
||||||
priority: number;
|
|
||||||
description: string;
|
|
||||||
JSONPath: string;
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
status: {
|
status: {
|
||||||
conditions: {
|
conditions: {
|
||||||
@ -61,8 +72,8 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getResourceApiBase() {
|
getResourceApiBase() {
|
||||||
const { version, group } = this.spec;
|
const { group } = this.spec;
|
||||||
return `/apis/${group}/${version}/${this.getPluralName()}`
|
return `/apis/${group}/${this.getVersion()}/${this.getPluralName()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
getPluralName() {
|
getPluralName() {
|
||||||
@ -87,7 +98,8 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getVersion() {
|
getVersion() {
|
||||||
return this.spec.version;
|
// v1 has removed the spec.version property, if it is present it must match the first version
|
||||||
|
return this.spec.versions[0]?.name ?? this.spec.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
isNamespaced() {
|
isNamespaced() {
|
||||||
@ -107,14 +119,16 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPrinterColumns(ignorePriority = true) {
|
getPrinterColumns(ignorePriority = true) {
|
||||||
const columns = this.spec.additionalPrinterColumns || [];
|
const columns = this.spec.versions.find(a => this.getVersion() == a.name)?.additionalPrinterColumns
|
||||||
|
?? this.spec.additionalPrinterColumns?.map(({JSONPath, ...rest}) => ({ ...rest, jsonPath: JSONPath })) // map to V1 shape
|
||||||
|
?? [];
|
||||||
return columns
|
return columns
|
||||||
.filter(column => column.name != "Age")
|
.filter(column => column.name != "Age")
|
||||||
.filter(column => ignorePriority ? true : !column.priority);
|
.filter(column => ignorePriority ? true : !column.priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
getValidation() {
|
getValidation() {
|
||||||
return JSON.stringify(this.spec.validation, null, 2);
|
return JSON.stringify(this.spec.validation ?? this.spec.versions?.[0]?.schema, null, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConditions() {
|
getConditions() {
|
||||||
@ -130,16 +144,10 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const crdBetaApi = new KubeApi<CustomResourceDefinition>({
|
export const crdApi = new VersionedKubeApi<CustomResourceDefinition>({
|
||||||
kind: CustomResourceDefinition.kind,
|
|
||||||
apiBase: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions",
|
|
||||||
isNamespaced: false,
|
|
||||||
objectConstructor: CustomResourceDefinition,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const crdApi = new KubeApi<CustomResourceDefinition>({
|
|
||||||
kind: CustomResourceDefinition.kind,
|
kind: CustomResourceDefinition.kind,
|
||||||
apiBase: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions",
|
apiBase: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions",
|
||||||
isNamespaced: false,
|
isNamespaced: false,
|
||||||
objectConstructor: CustomResourceDefinition,
|
objectConstructor: CustomResourceDefinition
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
56
src/renderer/api/kube-api-versioned.ts
Normal file
56
src/renderer/api/kube-api-versioned.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { stringify } from "querystring";
|
||||||
|
import { KubeObject } from "./kube-object";
|
||||||
|
import { createKubeApiURL } from "./kube-api-parse";
|
||||||
|
import { KubeApi, IKubeApiQueryParams, IKubeApiOptions } from "./kube-api";
|
||||||
|
import { apiManager } from "./api-manager";
|
||||||
|
|
||||||
|
export class VersionedKubeApi<T extends KubeObject = any> extends KubeApi<T> {
|
||||||
|
private preferredVersion?: string;
|
||||||
|
|
||||||
|
constructor(opts: IKubeApiOptions<T>) {
|
||||||
|
super(opts);
|
||||||
|
|
||||||
|
this.getPreferredVersion().then(() => {
|
||||||
|
if (this.apiBase != opts.apiBase)
|
||||||
|
apiManager.registerApi(this.apiBase, this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// override this property to make read-write
|
||||||
|
apiBase: string
|
||||||
|
|
||||||
|
async getPreferredVersion() {
|
||||||
|
if (this.preferredVersion) return;
|
||||||
|
|
||||||
|
const apiGroupVersion = await this.request.get<{ preferredVersion?: { version: string; }; }>(`${this.apiPrefix}/${this.apiGroup}`);
|
||||||
|
|
||||||
|
if (!apiGroupVersion?.preferredVersion) return;
|
||||||
|
|
||||||
|
this.preferredVersion = apiGroupVersion.preferredVersion.version;
|
||||||
|
|
||||||
|
// update apiBase
|
||||||
|
this.apiBase = this.getUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
async list({ namespace = "" } = {}, query?: IKubeApiQueryParams): Promise<T[]> {
|
||||||
|
await this.getPreferredVersion();
|
||||||
|
return await super.list({namespace}, query);
|
||||||
|
}
|
||||||
|
async get({ name = "", namespace = "default" } = {}, query?: IKubeApiQueryParams): Promise<T> {
|
||||||
|
await this.getPreferredVersion();
|
||||||
|
return super.get({ name, namespace }, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUrl({ name = "", namespace = "" } = {}, query?: Partial<IKubeApiQueryParams>) {
|
||||||
|
const { apiPrefix, apiGroup, apiVersion, apiResource, preferredVersion, isNamespaced } = this;
|
||||||
|
|
||||||
|
const resourcePath = createKubeApiURL({
|
||||||
|
apiPrefix: apiPrefix,
|
||||||
|
apiVersion: `${apiGroup}/${preferredVersion ?? apiVersion}`,
|
||||||
|
resource: apiResource,
|
||||||
|
namespace: isNamespaced ? namespace : undefined,
|
||||||
|
name: name,
|
||||||
|
});
|
||||||
|
return resourcePath + (query ? `?` + stringify(query) : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
.version {
|
.version {
|
||||||
.Select {
|
.Select {
|
||||||
width: 80px;
|
|
||||||
min-width: 80px;
|
min-width: 80px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import "./cluster-settings.scss";
|
import "./cluster-settings.scss";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Features } from "./features";
|
import { Features } from "./features";
|
||||||
import { Removal } from "./removal";
|
import { Removal } from "./removal";
|
||||||
@ -15,6 +14,25 @@ import { navigate } from "../../navigation";
|
|||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ClusterSettings extends React.Component {
|
export class ClusterSettings extends React.Component {
|
||||||
|
async componentDidMount() {
|
||||||
|
window.addEventListener('keydown', this.onEscapeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('keydown', this.onEscapeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEscapeKey = (evt: KeyboardEvent) => {
|
||||||
|
if (evt.code === "Escape") {
|
||||||
|
evt.stopPropagation();
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const cluster = getMatchedCluster();
|
const cluster = getMatchedCluster();
|
||||||
if (!cluster) return null;
|
if (!cluster) return null;
|
||||||
@ -26,7 +44,7 @@ export class ClusterSettings extends React.Component {
|
|||||||
showTooltip={false}
|
showTooltip={false}
|
||||||
/>
|
/>
|
||||||
<h2>{cluster.preferences.clusterName}</h2>
|
<h2>{cluster.preferences.clusterName}</h2>
|
||||||
<Icon material="close" onClick={() => navigate("/")} big/>
|
<Icon material="close" onClick={this.close} big/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export class Status extends React.Component<Props> {
|
|||||||
const rows = [
|
const rows = [
|
||||||
["Online Status", cluster.online ? "online" : `offline (${cluster.failureReason || "unknown reason"})`],
|
["Online Status", cluster.online ? "online" : `offline (${cluster.failureReason || "unknown reason"})`],
|
||||||
["Distribution", cluster.distribution],
|
["Distribution", cluster.distribution],
|
||||||
["Kerbel Version", cluster.version],
|
["Kernel Version", cluster.version],
|
||||||
["API Address", cluster.apiUrl],
|
["API Address", cluster.apiUrl],
|
||||||
["Nodes Count", cluster.nodes || "0"]
|
["Nodes Count", cluster.nodes || "0"]
|
||||||
];
|
];
|
||||||
|
|||||||
@ -102,13 +102,13 @@ export class CRDDetails extends React.Component<Props> {
|
|||||||
</TableHead>
|
</TableHead>
|
||||||
{
|
{
|
||||||
printerColumns.map((column, index) => {
|
printerColumns.map((column, index) => {
|
||||||
const { name, type, JSONPath } = column;
|
const { name, type, jsonPath } = column;
|
||||||
return (
|
return (
|
||||||
<TableRow key={index}>
|
<TableRow key={index}>
|
||||||
<TableCell className="name">{name}</TableCell>
|
<TableCell className="name">{name}</TableCell>
|
||||||
<TableCell className="type">{type}</TableCell>
|
<TableCell className="type">{type}</TableCell>
|
||||||
<TableCell className="json-path">
|
<TableCell className="json-path">
|
||||||
<Badge label={JSONPath}/>
|
<Badge label={jsonPath}/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -57,7 +57,7 @@ export class CrdResourceDetails extends React.Component<Props> {
|
|||||||
<KubeObjectMeta object={object}/>
|
<KubeObjectMeta object={object}/>
|
||||||
{extraColumns.map(column => {
|
{extraColumns.map(column => {
|
||||||
const { name } = column;
|
const { name } = column;
|
||||||
const value = jsonPath.query(object, column.JSONPath.slice(1));
|
const value = jsonPath.query(object, (column.jsonPath).slice(1));
|
||||||
return (
|
return (
|
||||||
<DrawerItem key={name} name={name}>
|
<DrawerItem key={name} name={name}>
|
||||||
<CrdColumnValue value={value} />
|
<CrdColumnValue value={value} />
|
||||||
|
|||||||
@ -57,7 +57,7 @@ export class CrdResources extends React.Component<Props> {
|
|||||||
[sortBy.age]: (item: KubeObject) => item.metadata.creationTimestamp,
|
[sortBy.age]: (item: KubeObject) => item.metadata.creationTimestamp,
|
||||||
}
|
}
|
||||||
extraColumns.forEach(column => {
|
extraColumns.forEach(column => {
|
||||||
sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.query(item, column.JSONPath.slice(1))
|
sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.query(item, column.jsonPath.slice(1))
|
||||||
})
|
})
|
||||||
// todo: merge extra columns and other params to predefined view
|
// todo: merge extra columns and other params to predefined view
|
||||||
const { List } = apiManager.getViews(crd.getResourceApiBase());
|
const { List } = apiManager.getViews(crd.getResourceApiBase());
|
||||||
@ -88,9 +88,9 @@ export class CrdResources extends React.Component<Props> {
|
|||||||
renderTableContents={(crdInstance: KubeObject) => [
|
renderTableContents={(crdInstance: KubeObject) => [
|
||||||
crdInstance.getName(),
|
crdInstance.getName(),
|
||||||
isNamespaced && crdInstance.getNs(),
|
isNamespaced && crdInstance.getNs(),
|
||||||
...extraColumns.map(column =>
|
...extraColumns.map(column => {
|
||||||
jsonPath.query(crdInstance, column.JSONPath.slice(1))
|
return jsonPath.query(crdInstance, (column.jsonPath).slice(1))
|
||||||
),
|
}),
|
||||||
crdInstance.getAge(),
|
crdInstance.getAge(),
|
||||||
]}
|
]}
|
||||||
renderItemMenu={(item: KubeObject) => {
|
renderItemMenu={(item: KubeObject) => {
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export class Preferences extends React.Component {
|
|||||||
@observable helmLoading = false;
|
@observable helmLoading = false;
|
||||||
@observable helmRepos: HelmRepo[] = [];
|
@observable helmRepos: HelmRepo[] = [];
|
||||||
@observable helmAddedRepos = observable.map<string, HelmRepo>();
|
@observable helmAddedRepos = observable.map<string, HelmRepo>();
|
||||||
|
@observable httpProxy = userStore.preferences.httpsProxy || "";
|
||||||
|
|
||||||
@computed get themeOptions(): SelectOption<string>[] {
|
@computed get themeOptions(): SelectOption<string>[] {
|
||||||
return themeStore.themes.map(theme => ({
|
return themeStore.themes.map(theme => ({
|
||||||
@ -39,9 +40,21 @@ export class Preferences extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
window.addEventListener('keydown', this.onEscapeKey);
|
||||||
await this.loadHelmRepos();
|
await this.loadHelmRepos();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('keydown', this.onEscapeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEscapeKey = (evt: KeyboardEvent) => {
|
||||||
|
if (evt.code === "Escape") {
|
||||||
|
evt.stopPropagation();
|
||||||
|
history.goBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async loadHelmRepos() {
|
async loadHelmRepos() {
|
||||||
this.helmLoading = true;
|
this.helmLoading = true;
|
||||||
@ -121,11 +134,13 @@ export class Preferences extends React.Component {
|
|||||||
<Input
|
<Input
|
||||||
theme="round-black"
|
theme="round-black"
|
||||||
placeholder={_i18n._(t`Type HTTP proxy url (example: http://proxy.acme.org:8080)`)}
|
placeholder={_i18n._(t`Type HTTP proxy url (example: http://proxy.acme.org:8080)`)}
|
||||||
value={preferences.httpsProxy || ""}
|
value={this.httpProxy}
|
||||||
onChange={v => preferences.httpsProxy = v}
|
onChange={v => this.httpProxy = v}
|
||||||
|
onBlur={() => preferences.httpsProxy = this.httpProxy}
|
||||||
/>
|
/>
|
||||||
<small className="hint">
|
<small className="hint">
|
||||||
<Trans>Proxy is used only for non-cluster communication.</Trans>
|
<Trans>Proxy is used only for non-cluster communication.</Trans>
|
||||||
|
</small>Proxy is used only for non-cluster communication.</Trans>
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
<KubectlBinaries preferences={preferences} />
|
<KubectlBinaries preferences={preferences} />
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.chart-version {
|
&.chart-version {
|
||||||
min-width: 80px;
|
min-width: 130px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,11 +70,15 @@ html {
|
|||||||
&__menu {
|
&__menu {
|
||||||
background: var(--select-menu-bgc);
|
background: var(--select-menu-bgc);
|
||||||
box-shadow: inset 0 0 0 1px var(--select-menu-border-color);
|
box-shadow: inset 0 0 0 1px var(--select-menu-border-color);
|
||||||
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
&-list {
|
&-list {
|
||||||
@include custom-scrollbar;
|
@include custom-scrollbar;
|
||||||
padding-right: 1px;
|
padding-right: 1px;
|
||||||
padding-left: 1px;
|
padding-left: 1px;
|
||||||
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-notice {
|
&-notice {
|
||||||
@ -83,6 +87,8 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__option {
|
&__option {
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background: $primary;
|
background: $primary;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user