forked from MapComplete/MapComplete
		
	Rework units to allow picking different default units in different locations, fixes #1011
This commit is contained in:
		
							parent
							
								
									e981abd2aa
								
							
						
					
					
						commit
						5da76b9418
					
				
					 17 changed files with 149 additions and 100 deletions
				
			
		|  | @ -8,14 +8,11 @@ export class Unit { | |||
|     public readonly appliesToKeys: Set<string>; | ||||
|     public readonly denominations: Denomination[]; | ||||
|     public readonly denominationsSorted: Denomination[]; | ||||
|     public readonly defaultDenom: Denomination; | ||||
|     public readonly eraseInvalid: boolean; | ||||
|     private readonly possiblePostFixes: string[] = [] | ||||
| 
 | ||||
|     constructor(appliesToKeys: string[], applicableUnits: Denomination[], eraseInvalid: boolean) { | ||||
|     constructor(appliesToKeys: string[], applicableDenominations: Denomination[], eraseInvalid: boolean) { | ||||
|         this.appliesToKeys = new Set(appliesToKeys); | ||||
|         this.denominations = applicableUnits; | ||||
|         this.defaultDenom = applicableUnits.filter(denom => denom.default)[0] | ||||
|         this.denominations = applicableDenominations; | ||||
|         this.eraseInvalid = eraseInvalid | ||||
| 
 | ||||
|         const seenUnitExtensions = new Set<string>(); | ||||
|  | @ -52,8 +49,6 @@ export class Unit { | |||
|             addPostfixesOf(denomination._canonicalSingular) | ||||
|             denomination.alternativeDenominations.forEach(addPostfixesOf) | ||||
|         } | ||||
|         this.possiblePostFixes = Array.from(possiblePostFixes) | ||||
|         this.possiblePostFixes.sort((a, b) => b.length - a.length) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -71,16 +66,12 @@ export class Unit { | |||
|         } | ||||
|         // Some keys do have unit handling
 | ||||
| 
 | ||||
|         const defaultSet = json.applicableUnits.filter(u => u.default === true) | ||||
|         // No default is defined - we pick the first as default
 | ||||
|         if (defaultSet.length === 0) { | ||||
|             json.applicableUnits[0].default = true | ||||
|         } | ||||
| 
 | ||||
|         // Check that there are not multiple defaults
 | ||||
|         if (defaultSet.length > 1) { | ||||
|             throw `Multiple units are set as default: they have canonical values of ${defaultSet.map(u => u.canonicalDenomination).join(", ")}` | ||||
|         if(json.applicableUnits.some(denom => denom.useAsDefaultInput !== undefined)){ | ||||
|             json.applicableUnits.forEach(denom => { | ||||
|                 denom.useAsDefaultInput = denom.useAsDefaultInput ?? false | ||||
|             }) | ||||
|         } | ||||
|          | ||||
|         const applicable = json.applicableUnits.map((u, i) => new Denomination(u, `${ctx}.units[${i}]`)) | ||||
|         return new Unit(appliesTo, applicable, json.eraseInvalidValues ?? false) | ||||
|     } | ||||
|  | @ -96,12 +87,13 @@ export class Unit { | |||
|     /** | ||||
|      * Finds which denomination is applicable and gives the stripped value back | ||||
|      */ | ||||
|     findDenomination(valueWithDenom: string): [string, Denomination] { | ||||
|     findDenomination(valueWithDenom: string, country: () => string): [string, Denomination] { | ||||
|         if (valueWithDenom === undefined) { | ||||
|             return undefined; | ||||
|         } | ||||
|         const defaultDenom = this.getDefaultDenomination(country) | ||||
|         for (const denomination of this.denominationsSorted) { | ||||
|             const bare = denomination.StrippedValue(valueWithDenom) | ||||
|             const bare = denomination.StrippedValue(valueWithDenom, defaultDenom === denomination) | ||||
|             if (bare !== null) { | ||||
|                 return [bare, denomination] | ||||
|             } | ||||
|  | @ -109,11 +101,11 @@ export class Unit { | |||
|         return [undefined, undefined] | ||||
|     } | ||||
| 
 | ||||
|     asHumanLongValue(value: string): BaseUIElement { | ||||
|     asHumanLongValue(value: string, country: () => string): BaseUIElement { | ||||
|         if (value === undefined) { | ||||
|             return undefined; | ||||
|         } | ||||
|         const [stripped, denom] = this.findDenomination(value) | ||||
|         const [stripped, denom] = this.findDenomination(value, country) | ||||
|         const human = stripped === "1" ? denom?.humanSingular : denom?.human | ||||
|         if (human === undefined) { | ||||
|             return new FixedUiElement(stripped ?? value); | ||||
|  | @ -124,24 +116,46 @@ export class Unit { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      *   Returns the value without any (sub)parts of any denomination - usefull as preprocessing step for validating inputs. | ||||
|      *   E.g. | ||||
|      *   if 'megawatt' is a possible denomination, then '5 Meg' will be rewritten to '5' (which can then be validated as a valid pnat) | ||||
|      * | ||||
|      *   Returns the original string if nothign matches | ||||
|      */ | ||||
|     stripUnitParts(str: string) { | ||||
|         if (str === undefined) { | ||||
|             return undefined; | ||||
|         } | ||||
| 
 | ||||
|         for (const denominationPart of this.possiblePostFixes) { | ||||
|             if (str.endsWith(denominationPart)) { | ||||
|                 return str.substring(0, str.length - denominationPart.length).trim() | ||||
|     public getDefaultInput(country: () => string | string[]) { | ||||
|         console.log("Searching the default denomination for input", country) | ||||
|         for (const denomination of this.denominations) { | ||||
|             if (denomination.useAsDefaultInput === true) { | ||||
|                 return denomination | ||||
|             } | ||||
|             if (denomination.useAsDefaultInput === undefined || denomination.useAsDefaultInput === false) { | ||||
|                 continue | ||||
|             } | ||||
|             let countries: string | string[] = country() | ||||
|             if (typeof countries === "string") { | ||||
|                 countries = countries.split(",") | ||||
|             } | ||||
|             const denominationCountries: string[] = denomination.useAsDefaultInput | ||||
|             if (countries.some(country => denominationCountries.indexOf(country) >= 0)) { | ||||
|                 return denomination | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return str; | ||||
|         return this.denominations[0] | ||||
|     } | ||||
|      | ||||
|     public getDefaultDenomination(country: () => string){ | ||||
|         for (const denomination of this.denominations) { | ||||
|             if (denomination.useIfNoUnitGiven === true || denomination.canonical === "") { | ||||
|                 return denomination | ||||
|             } | ||||
|             if (denomination.useIfNoUnitGiven === undefined || denomination.useIfNoUnitGiven === false) { | ||||
|                 continue | ||||
|             } | ||||
|             let countries: string | string[] = country() | ||||
|             if (typeof countries === "string") { | ||||
|                 countries = countries.split(",") | ||||
|             } | ||||
|             const denominationCountries: string[] = denomination.useIfNoUnitGiven | ||||
|             if (countries.some(country => denominationCountries.indexOf(country) >= 0)) { | ||||
|                 return denomination | ||||
|             } | ||||
|         } | ||||
|         return this.denominations[0] | ||||
|     } | ||||
| 
 | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue