Allow user to select Kubeconfig from filesystem (#740)

* Allow user to select Kubeconfig from filesystem, fix #738

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

Co-authored-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
Co-authored-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
authored and GitHub committed 2020-08-28 08:43:51 +03:00
1 parent 0a1f952e13
commit b8994a453f
24 files changed
+1113 -263

No files matched your search

@@ -0,0 +1,66 @@
import React, { InputHTMLAttributes } from "react";
export interface FileInputSelection<T = string> {
file: File;
data?: T | any; // not available when readAsTexts={false}
error?: string;
}
interface Props extends InputHTMLAttributes<any> {
id?: string; // could be used with <label htmlFor={id}/> to open filesystem dialog
accept?: string; // allowed file types to select, e.g. "application/json"
readAsText?: boolean; // provide files content as text in selection-callback
multiple?: boolean;
onSelectFiles(...selectedFiles: FileInputSelection[]): void;
}
export class FileInput extends React.Component<Props> {
protected input: HTMLInputElement;
protected style: React.CSSProperties = {
position: "absolute",
display: "none",
};
selectFiles = () => {
this.input.click(); // opens system dialog for selecting files
}
protected onChange = async (evt: React.ChangeEvent<HTMLInputElement>) => {
const fileList = Array.from(evt.target.files);
if (!fileList.length) {
return;
}
let selectedFiles: FileInputSelection[] = fileList.map(file => ({ file }));
if (this.props.readAsText) {
const readingFiles: Promise<FileInputSelection>[] = fileList.map(file => {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => {
resolve({
file: file,
data: reader.result,
error: reader.error ? String(reader.error) : null,
})
};
reader.readAsText(file);
})
});
selectedFiles = await Promise.all(readingFiles);
}
this.props.onSelectFiles(...selectedFiles);
}
render() {
const { onSelectFiles, readAsText, ...props } = this.props;
return (
<input
type="file"
style={this.style}
onChange={this.onChange}
ref={e => this.input = e}
{...props}
/>
)
}
}
+1
View File
@@ -1,2 +1,3 @@
export * from './input'
export * from './search-input'
export * from './file-input'
+12 -14
View File
@@ -17,6 +17,7 @@ export type InputProps<T = string> = Omit<InputElementProps, "onChange" | "onSub
theme?: "round-black";
className?: string;
value?: T;
autoSelectOnFocus?: boolean
multiLine?: boolean; // use text-area as input field
maxRows?: number; // when multiLine={true} define max rows size
dirty?: boolean; // show validation errors even if the field wasn't touched yet
@@ -112,8 +113,7 @@ export class Input extends React.Component<InputProps, State> {
const result = validator.validate(value, this.props);
if (isBoolean(result) && !result) {
errors.push(this.getValidatorError(value, validator));
}
else if (result instanceof Promise) {
} else if (result instanceof Promise) {
if (!validationId) {
this.validationId = validationId = uniqueId("validation_id_");
}
@@ -176,8 +176,9 @@ export class Input extends React.Component<InputProps, State> {
@autobind()
onFocus(evt: React.FocusEvent<InputElement>) {
const { onFocus } = this.props;
const { onFocus, autoSelectOnFocus } = this.props;
if (onFocus) onFocus(evt);
if (autoSelectOnFocus) this.select();
this.setState({ focused: true });
}
@@ -255,13 +256,14 @@ export class Input extends React.Component<InputProps, State> {
}
render() {
/* eslint-disable */
let { multiLine, showValidationLine, validators, theme, maxRows, children, iconLeft, iconRight, ...inputProps } = this.props;
let { className, maxLength, rows, disabled, } = this.props;
/* eslint-enable */
const {
multiLine, showValidationLine, validators, theme, maxRows, children,
maxLength, rows, disabled, autoSelectOnFocus, iconLeft, iconRight,
...inputProps
} = this.props;
const { focused, dirty, valid, validating, errors } = this.state;
className = cssNames("Input", className, {
const className = cssNames("Input", this.props.className, {
[`theme ${theme}`]: theme,
focused: focused,
disabled: disabled,
@@ -271,10 +273,6 @@ export class Input extends React.Component<InputProps, State> {
validatingLine: validating && showValidationLine,
});
// normalize icons
if (isString(iconLeft)) iconLeft = <Icon material={iconLeft}/>
if (isString(iconRight)) iconRight = <Icon material={iconRight}/>
// prepare input props
Object.assign(inputProps, {
className: "input box grow",
@@ -291,9 +289,9 @@ export class Input extends React.Component<InputProps, State> {
return (
<div className={className}>
<label className="input-area flex gaps align-center">
{iconLeft}
{isString(iconLeft) ? <Icon material={iconLeft}/> : iconLeft}
{multiLine ? <textarea {...inputProps as any}/> : <input {...inputProps as any}/>}
{iconRight}
{isString(iconRight) ? <Icon material={iconRight}/> : iconRight}
</label>
<div className="input-info flex gaps">
{!valid && dirty && (