forked from MapComplete/MapComplete
Reformat all files with prettier
This commit is contained in:
parent
e22d189376
commit
b541d3eab4
382 changed files with 50893 additions and 35566 deletions
|
|
@ -1,39 +1,38 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import {InputElement} from "./InputElement";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import {Store, UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Locale from "../i18n/Locale";
|
||||
import Combine from "../Base/Combine";
|
||||
import {TextField} from "./TextField";
|
||||
import Svg from "../../Svg";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
|
||||
import { UIElement } from "../UIElement"
|
||||
import { InputElement } from "./InputElement"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Locale from "../i18n/Locale"
|
||||
import Combine from "../Base/Combine"
|
||||
import { TextField } from "./TextField"
|
||||
import Svg from "../../Svg"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
|
||||
/**
|
||||
* A single 'pill' which can hide itself if the search criteria is not met
|
||||
*/
|
||||
class SelfHidingToggle extends UIElement implements InputElement<boolean> {
|
||||
private readonly _shown: BaseUIElement;
|
||||
private readonly _shown: BaseUIElement
|
||||
public readonly _selected: UIEventSource<boolean>
|
||||
public readonly isShown: Store<boolean> = new UIEventSource<boolean>(true);
|
||||
public readonly isShown: Store<boolean> = new UIEventSource<boolean>(true)
|
||||
public readonly forceSelected: UIEventSource<boolean>
|
||||
private readonly _squared: boolean;
|
||||
private readonly _squared: boolean
|
||||
public constructor(
|
||||
shown: string | BaseUIElement,
|
||||
mainTerm: Record<string, string>,
|
||||
search: Store<string>,
|
||||
options?: {
|
||||
searchTerms?: Record<string, string[]>,
|
||||
selected?: UIEventSource<boolean>,
|
||||
forceSelected?: UIEventSource<boolean>,
|
||||
searchTerms?: Record<string, string[]>
|
||||
selected?: UIEventSource<boolean>
|
||||
forceSelected?: UIEventSource<boolean>
|
||||
squared?: boolean
|
||||
}
|
||||
) {
|
||||
super();
|
||||
this._shown = Translations.W(shown);
|
||||
this._squared = options?.squared ?? false;
|
||||
const searchTerms: Record<string, string[]> = {};
|
||||
super()
|
||||
this._shown = Translations.W(shown)
|
||||
this._squared = options?.squared ?? false
|
||||
const searchTerms: Record<string, string[]> = {}
|
||||
for (const lng in options?.searchTerms ?? []) {
|
||||
if (lng === "_context") {
|
||||
continue
|
||||
|
|
@ -44,30 +43,34 @@ class SelfHidingToggle extends UIElement implements InputElement<boolean> {
|
|||
if (lng === "_context") {
|
||||
continue
|
||||
}
|
||||
const main = SelfHidingToggle.clean( mainTerm[lng])
|
||||
const main = SelfHidingToggle.clean(mainTerm[lng])
|
||||
searchTerms[lng] = [main].concat(searchTerms[lng] ?? [])
|
||||
}
|
||||
const selected = this._selected = options?.selected ?? new UIEventSource<boolean>(false);
|
||||
const forceSelected = this.forceSelected = options?.forceSelected ?? new UIEventSource<boolean>(false)
|
||||
this.isShown = search.map(s => {
|
||||
if (s === undefined || s.length === 0) {
|
||||
return true;
|
||||
}
|
||||
if (selected.data && !forceSelected.data) {
|
||||
return true
|
||||
}
|
||||
s = s?.trim()?.toLowerCase()
|
||||
if(searchTerms[Locale.language.data]?.some(t => t.indexOf(s) >= 0)){
|
||||
return true
|
||||
}
|
||||
if(searchTerms["*"]?.some(t => t.indexOf(s) >= 0)){
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
}, [selected, Locale.language])
|
||||
const selected = (this._selected = options?.selected ?? new UIEventSource<boolean>(false))
|
||||
const forceSelected = (this.forceSelected =
|
||||
options?.forceSelected ?? new UIEventSource<boolean>(false))
|
||||
this.isShown = search.map(
|
||||
(s) => {
|
||||
if (s === undefined || s.length === 0) {
|
||||
return true
|
||||
}
|
||||
if (selected.data && !forceSelected.data) {
|
||||
return true
|
||||
}
|
||||
s = s?.trim()?.toLowerCase()
|
||||
if (searchTerms[Locale.language.data]?.some((t) => t.indexOf(s) >= 0)) {
|
||||
return true
|
||||
}
|
||||
if (searchTerms["*"]?.some((t) => t.indexOf(s) >= 0)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
[selected, Locale.language]
|
||||
)
|
||||
|
||||
const self = this;
|
||||
this.isShown.addCallbackAndRun(shown => {
|
||||
const self = this
|
||||
this.isShown.addCallbackAndRun((shown) => {
|
||||
if (shown) {
|
||||
self.RemoveClass("hidden")
|
||||
} else {
|
||||
|
|
@ -75,25 +78,24 @@ class SelfHidingToggle extends UIElement implements InputElement<boolean> {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
private static clean(s: string) : string{
|
||||
|
||||
private static clean(s: string): string {
|
||||
return s?.trim()?.toLowerCase()?.replace(/[-]/, "")
|
||||
}
|
||||
|
||||
|
||||
GetValue(): UIEventSource<boolean> {
|
||||
return this._selected
|
||||
}
|
||||
|
||||
IsValid(t: boolean): boolean {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
protected InnerRender(): string | BaseUIElement {
|
||||
let el: BaseUIElement = this._shown;
|
||||
const selected = this._selected;
|
||||
let el: BaseUIElement = this._shown
|
||||
const selected = this._selected
|
||||
|
||||
selected.addCallbackAndRun(selected => {
|
||||
selected.addCallbackAndRun((selected) => {
|
||||
if (selected) {
|
||||
el.SetClass("border-4")
|
||||
el.RemoveClass("border")
|
||||
|
|
@ -107,77 +109,88 @@ class SelfHidingToggle extends UIElement implements InputElement<boolean> {
|
|||
|
||||
const forcedSelection = this.forceSelected
|
||||
el.onClick(() => {
|
||||
if(forcedSelection.data){
|
||||
if (forcedSelection.data) {
|
||||
selected.setData(true)
|
||||
}else{
|
||||
selected.setData(!selected.data);
|
||||
} else {
|
||||
selected.setData(!selected.data)
|
||||
}
|
||||
})
|
||||
|
||||
if(!this._squared){
|
||||
if (!this._squared) {
|
||||
el.SetClass("rounded-full")
|
||||
}
|
||||
return el.SetClass("border border-black p-1 px-4")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The searchable mappings selector is a selector which shows various pills from which one (or more) options can be chosen.
|
||||
* A searchfield can be used to filter the values
|
||||
*/
|
||||
export class SearchablePillsSelector<T> extends Combine implements InputElement<T[]> {
|
||||
private readonly selectedElements: UIEventSource<T[]>;
|
||||
private readonly selectedElements: UIEventSource<T[]>
|
||||
|
||||
public readonly someMatchFound: Store<boolean>;
|
||||
public readonly someMatchFound: Store<boolean>
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param values
|
||||
* @param options
|
||||
*/
|
||||
constructor(
|
||||
values: { show: BaseUIElement, value: T, mainTerm: Record<string, string>, searchTerms?: Record<string, string[]> }[],
|
||||
values: {
|
||||
show: BaseUIElement
|
||||
value: T
|
||||
mainTerm: Record<string, string>
|
||||
searchTerms?: Record<string, string[]>
|
||||
}[],
|
||||
options?: {
|
||||
mode?: "select-one" | "select-many",
|
||||
selectedElements?: UIEventSource<T[]>,
|
||||
searchValue?: UIEventSource<string>,
|
||||
onNoMatches?: BaseUIElement,
|
||||
onNoSearchMade?: BaseUIElement,
|
||||
mode?: "select-one" | "select-many"
|
||||
selectedElements?: UIEventSource<T[]>
|
||||
searchValue?: UIEventSource<string>
|
||||
onNoMatches?: BaseUIElement
|
||||
onNoSearchMade?: BaseUIElement
|
||||
/**
|
||||
* Shows this if there are many (>200) possible mappings
|
||||
*/
|
||||
onManyElements?: BaseUIElement,
|
||||
onManyElementsValue?: UIEventSource<T[]>,
|
||||
selectIfSingle?: false | boolean,
|
||||
searchAreaClass?: string,
|
||||
onManyElements?: BaseUIElement
|
||||
onManyElementsValue?: UIEventSource<T[]>
|
||||
selectIfSingle?: false | boolean
|
||||
searchAreaClass?: string
|
||||
hideSearchBar?: false | boolean
|
||||
}) {
|
||||
}
|
||||
) {
|
||||
const search = new TextField({ value: options?.searchValue })
|
||||
|
||||
const search = new TextField({value: options?.searchValue})
|
||||
const searchBar = options?.hideSearchBar
|
||||
? undefined
|
||||
: new Combine([
|
||||
Svg.search_svg().SetClass("w-8 normal-background"),
|
||||
search.SetClass("w-full"),
|
||||
]).SetClass("flex items-center border-2 border-black m-2")
|
||||
|
||||
const searchBar = options?.hideSearchBar ? undefined : new Combine([Svg.search_svg().SetClass("w-8 normal-background"), search.SetClass("w-full")])
|
||||
.SetClass("flex items-center border-2 border-black m-2")
|
||||
|
||||
const searchValue = search.GetValue().map(s => s?.trim()?.toLowerCase())
|
||||
const selectedElements = options?.selectedElements ?? new UIEventSource<T[]>([]);
|
||||
const mode = options?.mode ?? "select-one";
|
||||
const searchValue = search.GetValue().map((s) => s?.trim()?.toLowerCase())
|
||||
const selectedElements = options?.selectedElements ?? new UIEventSource<T[]>([])
|
||||
const mode = options?.mode ?? "select-one"
|
||||
const onEmpty = options?.onNoMatches ?? Translations.t.general.noMatchingMapping
|
||||
|
||||
const mappedValues: { show: SelfHidingToggle, mainTerm: Record<string, string>, value: T }[] = values.map(v => {
|
||||
const mappedValues: {
|
||||
show: SelfHidingToggle
|
||||
mainTerm: Record<string, string>
|
||||
value: T
|
||||
}[] = values.map((v) => {
|
||||
const vIsSelected = new UIEventSource(false)
|
||||
|
||||
const vIsSelected = new UIEventSource(false);
|
||||
|
||||
selectedElements.addCallbackAndRunD(selectedElements => {
|
||||
vIsSelected.setData(selectedElements.some(t => t === v.value))
|
||||
selectedElements.addCallbackAndRunD((selectedElements) => {
|
||||
vIsSelected.setData(selectedElements.some((t) => t === v.value))
|
||||
})
|
||||
|
||||
vIsSelected.addCallback(selected => {
|
||||
vIsSelected.addCallback((selected) => {
|
||||
if (selected) {
|
||||
if (mode === "select-one") {
|
||||
selectedElements.setData([v.value])
|
||||
} else if (!selectedElements.data.some(t => t === v.value)) {
|
||||
selectedElements.data.push(v.value);
|
||||
} else if (!selectedElements.data.some((t) => t === v.value)) {
|
||||
selectedElements.data.push(v.value)
|
||||
selectedElements.ping()
|
||||
}
|
||||
} else {
|
||||
|
|
@ -186,7 +199,7 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement<
|
|||
if (t == v.value) {
|
||||
selectedElements.data.splice(i, 1)
|
||||
selectedElements.ping()
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,89 +208,99 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement<
|
|||
const toggle = new SelfHidingToggle(v.show, v.mainTerm, searchValue, {
|
||||
searchTerms: v.searchTerms,
|
||||
selected: vIsSelected,
|
||||
squared: mode === "select-many"
|
||||
squared: mode === "select-many",
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
...v,
|
||||
show: toggle
|
||||
};
|
||||
show: toggle,
|
||||
}
|
||||
})
|
||||
|
||||
let totalShown: Store<number>
|
||||
if (options.selectIfSingle) {
|
||||
let forcedSelection : { value: T, show: SelfHidingToggle } = undefined
|
||||
totalShown = searchValue.map(_ => {
|
||||
let totalShown = 0;
|
||||
let lastShownValue: { value: T, show: SelfHidingToggle }
|
||||
for (const mv of mappedValues) {
|
||||
const valueIsShown = mv.show.isShown.data
|
||||
if (valueIsShown) {
|
||||
totalShown++;
|
||||
lastShownValue = mv
|
||||
let forcedSelection: { value: T; show: SelfHidingToggle } = undefined
|
||||
totalShown = searchValue.map(
|
||||
(_) => {
|
||||
let totalShown = 0
|
||||
let lastShownValue: { value: T; show: SelfHidingToggle }
|
||||
for (const mv of mappedValues) {
|
||||
const valueIsShown = mv.show.isShown.data
|
||||
if (valueIsShown) {
|
||||
totalShown++
|
||||
lastShownValue = mv
|
||||
}
|
||||
}
|
||||
}
|
||||
if (totalShown == 1) {
|
||||
if (selectedElements.data?.indexOf(lastShownValue.value) < 0) {
|
||||
selectedElements.setData([lastShownValue.value])
|
||||
lastShownValue.show.forceSelected.setData(true)
|
||||
forcedSelection = lastShownValue
|
||||
if (totalShown == 1) {
|
||||
if (selectedElements.data?.indexOf(lastShownValue.value) < 0) {
|
||||
selectedElements.setData([lastShownValue.value])
|
||||
lastShownValue.show.forceSelected.setData(true)
|
||||
forcedSelection = lastShownValue
|
||||
}
|
||||
} else if (forcedSelection != undefined) {
|
||||
forcedSelection?.show?.forceSelected?.setData(false)
|
||||
forcedSelection = undefined
|
||||
selectedElements.setData([])
|
||||
}
|
||||
} else if (forcedSelection != undefined) {
|
||||
forcedSelection?.show?.forceSelected?.setData(false)
|
||||
forcedSelection = undefined;
|
||||
selectedElements.setData([])
|
||||
}
|
||||
|
||||
return totalShown
|
||||
}, mappedValues.map(mv => mv.show.GetValue()))
|
||||
return totalShown
|
||||
},
|
||||
mappedValues.map((mv) => mv.show.GetValue())
|
||||
)
|
||||
} else {
|
||||
totalShown = searchValue.map(_ => mappedValues.filter(mv => mv.show.isShown.data).length, mappedValues.map(mv => mv.show.GetValue()))
|
||||
|
||||
totalShown = searchValue.map(
|
||||
(_) => mappedValues.filter((mv) => mv.show.isShown.data).length,
|
||||
mappedValues.map((mv) => mv.show.GetValue())
|
||||
)
|
||||
}
|
||||
const tooMuchElementsCutoff = 200;
|
||||
options?.onManyElementsValue?.map(value => {
|
||||
console.log("Installing toMuchElementsValue", value)
|
||||
if(tooMuchElementsCutoff <= totalShown.data){
|
||||
selectedElements.setData(value)
|
||||
selectedElements.ping()
|
||||
}
|
||||
}, [totalShown])
|
||||
const tooMuchElementsCutoff = 200
|
||||
options?.onManyElementsValue?.map(
|
||||
(value) => {
|
||||
console.log("Installing toMuchElementsValue", value)
|
||||
if (tooMuchElementsCutoff <= totalShown.data) {
|
||||
selectedElements.setData(value)
|
||||
selectedElements.ping()
|
||||
}
|
||||
},
|
||||
[totalShown]
|
||||
)
|
||||
|
||||
super([
|
||||
searchBar,
|
||||
new VariableUiElement(Locale.language.map(lng => {
|
||||
if(totalShown.data >= 200){
|
||||
return options?.onManyElements ?? Translations.t.general.useSearch;
|
||||
}
|
||||
if (options?.onNoSearchMade !== undefined && (searchValue.data === undefined || searchValue.data.length === 0)) {
|
||||
return options?.onNoSearchMade
|
||||
}
|
||||
if (totalShown.data == 0) {
|
||||
return onEmpty
|
||||
}
|
||||
|
||||
mappedValues.sort((a, b) => a.mainTerm[lng] < b.mainTerm[lng] ? -1 : 1)
|
||||
return new Combine(mappedValues.map(e => e.show))
|
||||
.SetClass("flex flex-wrap w-full content-start")
|
||||
.SetClass(options?.searchAreaClass ?? "")
|
||||
}, [totalShown, searchValue]))
|
||||
new VariableUiElement(
|
||||
Locale.language.map(
|
||||
(lng) => {
|
||||
if (totalShown.data >= 200) {
|
||||
return options?.onManyElements ?? Translations.t.general.useSearch
|
||||
}
|
||||
if (
|
||||
options?.onNoSearchMade !== undefined &&
|
||||
(searchValue.data === undefined || searchValue.data.length === 0)
|
||||
) {
|
||||
return options?.onNoSearchMade
|
||||
}
|
||||
if (totalShown.data == 0) {
|
||||
return onEmpty
|
||||
}
|
||||
|
||||
mappedValues.sort((a, b) => (a.mainTerm[lng] < b.mainTerm[lng] ? -1 : 1))
|
||||
return new Combine(mappedValues.map((e) => e.show))
|
||||
.SetClass("flex flex-wrap w-full content-start")
|
||||
.SetClass(options?.searchAreaClass ?? "")
|
||||
},
|
||||
[totalShown, searchValue]
|
||||
)
|
||||
),
|
||||
])
|
||||
this.selectedElements = selectedElements;
|
||||
this.someMatchFound = totalShown.map(t => t > 0);
|
||||
|
||||
this.selectedElements = selectedElements
|
||||
this.someMatchFound = totalShown.map((t) => t > 0)
|
||||
}
|
||||
|
||||
public GetValue(): UIEventSource<T[]> {
|
||||
return this.selectedElements;
|
||||
return this.selectedElements
|
||||
}
|
||||
|
||||
IsValid(t: T[]): boolean {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue