mapcomplete/UI/Input/MultiInput.ts

125 lines
4.3 KiB
TypeScript
Raw Normal View History

2020-09-02 11:37:34 +02:00
import {InputElement} from "./InputElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import {UIElement} from "../UIElement";
import Combine from "../Base/Combine";
import {SubtleButton} from "../Base/SubtleButton";
2020-11-06 01:58:26 +01:00
import Svg from "../../Svg";
2020-09-02 11:37:34 +02:00
export class MultiInput<T> extends InputElement<T[]> {
private readonly _value: UIEventSource<T[]>;
IsSelected: UIEventSource<boolean>;
private elements: UIElement[] = [];
private inputELements: InputElement<T>[] = [];
private addTag: UIElement;
private _options: { allowMovement?: boolean };
2020-09-02 11:37:34 +02:00
constructor(
addAElement: string,
newElement: (() => T),
createInput: (() => InputElement<T>),
value: UIEventSource<T[]> = undefined,
options?: {
allowMovement?: boolean
}) {
2020-09-02 11:37:34 +02:00
super(undefined);
this._value = value ?? new UIEventSource<T[]>([]);
value = this._value;
2020-09-14 20:16:03 +02:00
this.ListenTo(value.map((latest : T[]) => latest.length));
this._options = options ?? {};
2020-09-02 11:37:34 +02:00
2020-11-06 04:02:53 +01:00
this.addTag = new SubtleButton(Svg.addSmall_ui(), addAElement)
2020-09-02 11:37:34 +02:00
.SetClass("small-button")
.onClick(() => {
this.IsSelected.setData(true);
value.data.push(newElement());
value.ping();
});
const self = this;
value.map<number>((tags: string[]) => tags.length).addCallback(() => self.createElements(createInput));
this.createElements(createInput);
this._value.addCallback(tags => self.load(tags));
this.IsSelected = new UIEventSource<boolean>(false);
}
private load(tags: T[]) {
if (tags === undefined) {
return;
}
for (let i = 0; i < tags.length; i++) {
this.inputELements[i].GetValue().setData(tags[i]);
}
}
private UpdateIsSelected(){
this.IsSelected.setData(this.inputELements.map(input => input.IsSelected.data).reduce((a,b) => a && b))
}
private createElements(createInput: (() => InputElement<T>)) {
this.inputELements.splice(0, this.inputELements.length);
this.elements = [];
const self = this;
for (let i = 0; i < this._value.data.length; i++) {
const input = createInput();
input.GetValue().addCallback(tag => {
self._value.data[i] = tag;
self._value.ping();
}
);
this.inputELements.push(input);
input.IsSelected.addCallback(() => this.UpdateIsSelected());
2020-11-06 04:02:53 +01:00
const moveUpBtn = Svg.up_ui()
.onClick(() => {
const v = self._value.data[i];
self._value.data[i] = self._value.data[i - 1];
self._value.data[i - 1] = v;
self._value.ping();
});
2020-11-06 03:17:27 +01:00
const moveDownBtn =
Svg.down_ui().SetStyle('max-width: 1.5em; margin-left: 5px;display:block;')
.onClick(() => {
const v = self._value.data[i];
self._value.data[i] = self._value.data[i + 1];
self._value.data[i + 1] = v;
self._value.ping();
});
const controls = [];
if (i > 0 && this._options.allowMovement) {
controls.push(moveUpBtn);
}
if (i + 1 < this._value.data.length && this._options.allowMovement) {
controls.push(moveDownBtn);
}
2020-11-06 03:17:27 +01:00
const deleteBtn =
Svg.delete_icon_ui().SetStyle('max-width: 1.5em;width:1.5em; margin-left: 5px;')
2020-09-02 11:37:34 +02:00
.onClick(() => {
self._value.data.splice(i, 1);
self._value.ping();
});
controls.push(deleteBtn);
this.elements.push(new Combine([input.SetStyle("width: calc(100% - 2em - 5px)"), new Combine(controls).SetStyle("display:flex;flex-direction:column;width:min-content;")]).SetClass("tag-input-row"))
2020-09-02 11:37:34 +02:00
}
this.Update();
}
InnerRender(): string {
return new Combine([...this.elements, this.addTag]).Render();
}
IsValid(t: T[]): boolean {
return false;
}
GetValue(): UIEventSource<T[]> {
return this._value;
}
}