Add FieldList field type with subtype FieldConfigList

pull/374/head
Kelvin Schoofs 2 years ago
parent 5900185d37
commit 796d1c245b

@ -55,6 +55,7 @@
- Caching of Yarn dependencies is now handled by `actions/setup-node` - Caching of Yarn dependencies is now handled by `actions/setup-node`
- Migrated from `actions/create-release` and `actions/upload-release-asset` to `softprops/action-gh-release` - Migrated from `actions/create-release` and `actions/upload-release-asset` to `softprops/action-gh-release`
- Fix Webpack only listening on IPv6 instead of all interfaces + update VS Code default styles - Fix Webpack only listening on IPv6 instead of all interfaces + update VS Code default styles
- Added the `FieldList` and derivate `FieldConfigList` field types to the Settings UI
## v1.25.0 (2022-06-01) ## v1.25.0 (2022-06-01)

@ -3,7 +3,7 @@ import { FieldGroup } from './group';
import './index.css'; import './index.css';
export interface Props<T> { export interface Props<T> {
label: string; label?: string;
description?: string; description?: string;
value: T; value: T;
optional?: boolean; optional?: boolean;
@ -59,8 +59,8 @@ export abstract class FieldBase<T, P extends {} = {}, S extends {} = {}> extends
} }
return newValue!; return newValue!;
} }
public getLabel() { public getLabel(): string {
return this.props.label; return this.props.label || '';
} }
protected getClassName(): string { return 'Field'; } protected getClassName(): string { return 'Field'; }
protected getValueClassName(): string { return 'value'; } protected getValueClassName(): string { return 'value'; }
@ -69,7 +69,7 @@ export abstract class FieldBase<T, P extends {} = {}, S extends {} = {}> extends
const { description, label, optional, preface, postface } = this.props; const { description, label, optional, preface, postface } = this.props;
return <div className={this.getClassName()}> return <div className={this.getClassName()}>
<FieldGroup.Consumer>{group => (group?.register(this), [])}</FieldGroup.Consumer> <FieldGroup.Consumer>{group => (group?.register(this), [])}</FieldGroup.Consumer>
<div className="label">{label}</div>{optional && <div className="optional">Optional</div>} {label && <><div className="label">{label}</div>{optional && <div className="optional">Optional</div>}</>}
{description && <div className="description">{description}</div>} {description && <div className="description">{description}</div>}
{preface} {preface}
{error && <div className="error">{error}</div>} {error && <div className="error">{error}</div>}

@ -117,6 +117,40 @@ div.FieldDropdownWithInput p.arrow {
cursor: pointer; cursor: pointer;
} }
div.FieldList > div.adder {
display: flex;
}
div.FieldList > div.adder > div.Field {
padding: 0;
flex-grow: 1;
}
div.FieldList button {
height: 1em;
padding: 5px;
margin: 9px 0 0 5px;
box-sizing: content-box;
border-radius: 0;
}
div.FieldList > li {
background: var(--vscode-settings-dropdownBackground);
color: var(--vscode-settings-dropdownForeground);
border: 1px solid var(--vscode-settings-dropdownBorder);
position: relative;
display: flex;
height: 1em;
padding: 5px;
margin: 5px 0;
}
div.FieldList > li > p {
margin: auto;
flex-grow: 1;
}
div.FieldList > li > button {
margin: -4px;
padding: 3px;
height: 100%;
}
div.FieldPath { div.FieldPath {
position: relative; position: relative;
background: none; background: none;

@ -0,0 +1,64 @@
import * as React from 'react';
import { connect } from '../redux';
import { FieldBase } from './base';
import { FieldDropdown } from './dropdown';
import { FieldDropdownWithInput } from './dropdownwithinput';
import { FieldString } from './string';
// Maybe in the future we can make this generic, but we'd have to make FieldDropdown etc also generic first
type T = string;
// TODO: Allow reordering of items
export interface Props<T> {
options?: T[];
freeText?: boolean;
//displayName?(item: T): string;
displayStyle?(item: T): React.CSSProperties;
}
interface State {
open: boolean;
inputText: string;
}
export class FieldList extends FieldBase<T[] | undefined, Props<T>, State> {
public getInitialSubState(props: Props<string>): State {
return { open: false, inputText: '' };
}
protected renderNewInputField(): React.ReactNode {
const { inputText } = this.state;
const { freeText, options, displayStyle } = this.props;
const FD = options?.length ? (freeText ? FieldDropdownWithInput : FieldDropdown) : (freeText ? FieldString : undefined);
return FD && <FD value={inputText} values={options} displayStyle={displayStyle}
onChange={t => this.setState({ inputText: t || '' })} />;
}
public renderInput(): React.ReactNode {
const { newValue } = this.state;
const { displayStyle } = this.props;
const newInput = this.renderNewInputField();
return <div className="FieldList">
{newInput && <div className="adder">{newInput}<button onClick={this.onAdd}>+</button></div>}
{newValue?.map((item, index) => <li key={index} style={displayStyle?.(item)}>
<p>{item}</p>
<button onClick={this.onRemove.bind(this, index)}>x</button>
</li>)}
</div>;
}
protected onAdd = () => {
const inputText = this.state.inputText?.trim();
if (!inputText) return;
this.setState({
inputText: '',
newValue: [...this.state.newValue || [], inputText]
}, () => this.props.onChange(this.state.newValue));
};
protected onRemove = (index: number) => {
const newValue = [...this.state.newValue || []];
newValue.splice(index, 1);
this.setState({ newValue }, () => this.props.onChange(newValue));
};
}
export type FieldConfigListState = { options: string[] };
export const FieldConfigList = connect(FieldList)<FieldConfigListState>(
state => ({ options: state.data.configs.map(c => c.name).sort() }),
);
Loading…
Cancel
Save