forked from MapComplete/MapComplete
		
	First version of the OH-input-element
This commit is contained in:
		
							parent
							
								
									b93f25d79c
								
							
						
					
					
						commit
						895ec01213
					
				
					 16 changed files with 532 additions and 248 deletions
				
			
		|  | @ -22,21 +22,22 @@ export class OH { | |||
|         su: 6 | ||||
|     } | ||||
| 
 | ||||
|     public static hhmm(h: number, m: number): string { | ||||
|         if (h == 24) { | ||||
|             return "00:00"; | ||||
|         } | ||||
|         return Utils.TwoDigits(h) + ":" + Utils.TwoDigits(m); | ||||
|     } | ||||
| 
 | ||||
|     public static ToString(ohs: OpeningHour[]) { | ||||
|         if (ohs.length == 0) { | ||||
|             return ""; | ||||
|         } | ||||
|         const partsPerWeekday: string [][] = [[], [], [], [], [], [], []]; | ||||
| 
 | ||||
|         function hhmm(h, m) { | ||||
|             if (h == 24) { | ||||
|                 return "00:00"; | ||||
|             } | ||||
|             return Utils.TwoDigits(h) + ":" + Utils.TwoDigits(m); | ||||
|         } | ||||
| 
 | ||||
|         for (const oh of ohs) { | ||||
|             partsPerWeekday[oh.weekday].push(hhmm(oh.startHour, oh.startMinutes) + "-" + hhmm(oh.endHour, oh.endMinutes)); | ||||
|             partsPerWeekday[oh.weekday].push(OH.hhmm(oh.startHour, oh.startMinutes) + "-" + OH.hhmm(oh.endHour, oh.endMinutes)); | ||||
|         } | ||||
| 
 | ||||
|         const stringPerWeekday = partsPerWeekday.map(parts => parts.sort().join(", ")); | ||||
|  | @ -72,8 +73,8 @@ export class OH { | |||
|         } | ||||
|         pushRule(); | ||||
| 
 | ||||
|         const oh = rules.join("; ") + ";" | ||||
|         if (oh === "Mo-Su 00:00-00:00;") { | ||||
|         const oh = rules.join("; ") | ||||
|         if (oh === "Mo-Su 00:00-00:00") { | ||||
|             return "24/7" | ||||
|         } | ||||
|         return oh; | ||||
|  | @ -162,11 +163,11 @@ export class OH { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private static startTime(oh: OpeningHour): number { | ||||
|     public static startTime(oh: OpeningHour): number { | ||||
|         return oh.startHour + oh.startMinutes / 60; | ||||
|     } | ||||
| 
 | ||||
|     private static endTime(oh: OpeningHour): number { | ||||
|     public static endTime(oh: OpeningHour): number { | ||||
|         return oh.endHour + oh.endMinutes / 60; | ||||
|     } | ||||
| 
 | ||||
|  | @ -181,20 +182,26 @@ export class OH { | |||
|     } | ||||
| 
 | ||||
|     private static parseHHMM(hhmm: string): { hours: number, minutes: number } { | ||||
|         if(hhmm === undefined || hhmm == null){ | ||||
|             return null; | ||||
|         } | ||||
|         const spl = hhmm.trim().split(":"); | ||||
|         if(spl.length != 2){ | ||||
|             return null; | ||||
|         } | ||||
|         return {hours: Number(spl[0].trim()), minutes: Number(spl[1].trim())}; | ||||
|     } | ||||
| 
 | ||||
|     private static parseHHMMRange(hhmmhhmm: string): { | ||||
|     public static parseHHMMRange(hhmmhhmm: string): { | ||||
|         startHour: number, | ||||
|         startMinutes: number, | ||||
|         endHour: number, | ||||
|         endMinutes: number | ||||
|     } { | ||||
|         if(hhmmhhmm == "off"){ | ||||
|         if (hhmmhhmm == "off") { | ||||
|             return null; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         const timings = hhmmhhmm.split("-"); | ||||
|         const start = OH.parseHHMM(timings[0]) | ||||
|         const end = OH.parseHHMM(timings[1]); | ||||
|  | @ -212,6 +219,9 @@ export class OH { | |||
|         endHour: number, | ||||
|         endMinutes: number | ||||
|     }[] { | ||||
|         if (hhmms === "off") { | ||||
|             return []; | ||||
|         } | ||||
|         return hhmms.split(",") | ||||
|             .map(s => s.trim()) | ||||
|             .filter(str => str !== "") | ||||
|  | @ -226,17 +236,24 @@ export class OH { | |||
|     private static ParseWeekdayRange(weekdays: string): number[] { | ||||
|         const split = weekdays.split("-"); | ||||
|         if (split.length == 1) { | ||||
|             return [OH.ParseWeekday(weekdays)]; | ||||
|             const parsed = OH.ParseWeekday(weekdays); | ||||
|             if(parsed == null){ | ||||
|                 return null; | ||||
|             } | ||||
|             return [parsed]; | ||||
|         } else if (split.length == 2) { | ||||
|             let start = OH.ParseWeekday(split[0]); | ||||
|             let end = OH.ParseWeekday(split[1]); | ||||
|             if ((start ?? null) === null || (end ?? null) === null) { | ||||
|                 return null; | ||||
|             } | ||||
|             let range = []; | ||||
|             for (let i = start; i <= end; i++) { | ||||
|                 range.push(i); | ||||
|             } | ||||
|             return range; | ||||
|         } else { | ||||
|             throw "Invalid format: " + weekdays + " is not a weekdays range" | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -244,12 +261,19 @@ export class OH { | |||
|         let ranges = []; | ||||
|         let split = weekdays.split(","); | ||||
|         for (const weekday of split) { | ||||
|             ranges.push(...OH.ParseWeekdayRange(weekday)); | ||||
|             const parsed = OH.ParseWeekdayRange(weekday) | ||||
|             if (parsed === undefined || parsed === null) { | ||||
|                 return null; | ||||
|             } | ||||
|             ranges.push(...parsed); | ||||
|         } | ||||
|         return ranges; | ||||
|     } | ||||
| 
 | ||||
|     private static multiply(weekdays: number[], timeranges: { startHour: number, startMinutes: number, endHour: number, endMinutes: number }[]) { | ||||
|         if ((weekdays ?? null) == null || (timeranges ?? null) == null) { | ||||
|             return null; | ||||
|         } | ||||
|         const ohs: OpeningHour[] = [] | ||||
|         for (const timerange of timeranges) { | ||||
|             for (const weekday of weekdays) { | ||||
|  | @ -264,24 +288,34 @@ export class OH { | |||
|     } | ||||
| 
 | ||||
|     public static ParseRule(rule: string): OpeningHour[] { | ||||
|         if (rule.trim() == "24/7") { | ||||
|             return OH.multiply([0, 1, 2, 3, 4, 5, 6], [{startHour: 0, startMinutes: 0, endHour: 24, endMinutes: 0}]); | ||||
|         } | ||||
|         try { | ||||
|             if (rule.trim() == "24/7") { | ||||
|                 return OH.multiply([0, 1, 2, 3, 4, 5, 6], [{ | ||||
|                     startHour: 0, | ||||
|                     startMinutes: 0, | ||||
|                     endHour: 24, | ||||
|                     endMinutes: 0 | ||||
|                 }]); | ||||
|             } | ||||
| 
 | ||||
|         const split = rule.trim().replace(/, */g, ",").split(" "); | ||||
|         if (split.length == 1) { | ||||
|             // First, try to parse this rule as a rule without weekdays
 | ||||
|             let timeranges = OH.ParseHhmmRanges(rule); | ||||
|             let weekdays = [0, 1, 2, 3, 4, 5, 6]; | ||||
|             return OH.multiply(weekdays, timeranges); | ||||
|         } | ||||
|             const split = rule.trim().replace(/, */g, ",").split(" "); | ||||
|             if (split.length == 1) { | ||||
|                 // First, try to parse this rule as a rule without weekdays
 | ||||
|                 let timeranges = OH.ParseHhmmRanges(rule); | ||||
|                 let weekdays = [0, 1, 2, 3, 4, 5, 6]; | ||||
|                 return OH.multiply(weekdays, timeranges); | ||||
|             } | ||||
| 
 | ||||
|         if (split.length == 2) { | ||||
|             const weekdays = OH.ParseWeekdayRanges(split[0]); | ||||
|             const timeranges = OH.ParseHhmmRanges(split[1]); | ||||
|             return OH.multiply(weekdays, timeranges); | ||||
|             if (split.length == 2) { | ||||
|                 const weekdays = OH.ParseWeekdayRanges(split[0]); | ||||
|                 const timeranges = OH.ParseHhmmRanges(split[1]); | ||||
|                 return OH.multiply(weekdays, timeranges); | ||||
|             } | ||||
|             return null; | ||||
|         } catch (e) { | ||||
|             console.log("Could not parse weekday rule ", rule); | ||||
|             return null; | ||||
|         } | ||||
|         throw `Could not parse rule: ${rule} has ${split.length} parts (expected one or two)`; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -299,7 +333,10 @@ export class OH { | |||
|                 continue; | ||||
|             } | ||||
|             try { | ||||
|                 ohs.push(...OH.ParseRule(rule)); | ||||
|                 const parsed = OH.ParseRule(rule) | ||||
|                 if (parsed !== null) { | ||||
|                     ohs.push(...parsed); | ||||
|                 } | ||||
|             } catch (e) { | ||||
|                 console.error("Could not parse ", rule, ": ", e) | ||||
|             } | ||||
|  |  | |||
|  | @ -49,7 +49,7 @@ export default class SavePanel extends UIElement { | |||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return new Combine([ | ||||
|             "<h3>Saving</h3>", | ||||
|             "<h3>Save your theme</h3>", | ||||
|             this.lastSaveEl, | ||||
|             "<h3>JSON configuration</h3>", | ||||
|             "The url hash is actually no more then a BASE64-encoding of the below JSON. This json contains the full configuration of the theme.<br/>" + | ||||
|  |  | |||
							
								
								
									
										116
									
								
								UI/Input/OpeningHours/OpeningHoursInput.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								UI/Input/OpeningHours/OpeningHoursInput.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | |||
| import {InputElement} from "../InputElement"; | ||||
| import {UIEventSource} from "../../../Logic/UIEventSource"; | ||||
| import {UIElement} from "../../UIElement"; | ||||
| import Combine from "../../Base/Combine"; | ||||
| import {OH} from "../../../Logic/OpeningHours"; | ||||
| import OpeningHoursPicker from "./OpeningHoursPicker"; | ||||
| import {VariableUiElement} from "../../Base/VariableUIElement"; | ||||
| import Translations from "../../i18n/Translations"; | ||||
| import {FixedUiElement} from "../../Base/FixedUiElement"; | ||||
| import PublicHolidayInput from "./PublicHolidayInput"; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * The full opening hours element, including the table, opening hours picker. | ||||
|  * Keeps track of unparsed rules | ||||
|  * Exports everything conventiently as a string, for direct use | ||||
|  */ | ||||
| export default class OpeningHoursInput extends InputElement<string> { | ||||
| 
 | ||||
| 
 | ||||
|     private readonly _value: UIEventSource<string>; | ||||
| 
 | ||||
|     private readonly _ohPicker: UIElement; | ||||
|     private readonly _leftoverWarning: UIElement; | ||||
|     private readonly _phSelector: UIElement; | ||||
| 
 | ||||
|     constructor(value: UIEventSource<string> = new UIEventSource<string>("")) { | ||||
|         super(); | ||||
| 
 | ||||
|         const rulesFromOhPicker = value.map(OH.Parse); | ||||
| 
 | ||||
|         const leftoverRules = value.map<string[]>(str => { | ||||
|             if (str === undefined) { | ||||
|                 return [] | ||||
|             } | ||||
|             const leftOvers: string[] = []; | ||||
|             const rules = str.split(";"); | ||||
|             for (const rule of rules) { | ||||
|                 if (OH.ParseRule(rule) !== null) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (PublicHolidayInput.LoadValue(rule) !== null) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 leftOvers.push(rule); | ||||
|             } | ||||
|             return leftOvers; | ||||
|         }) | ||||
| 
 | ||||
|         const ph = value.map<string>(str => { | ||||
|             if (str === undefined) { | ||||
|                 return "" | ||||
|             } | ||||
|             const rules = str.split(";"); | ||||
|             for (const rule of rules) { | ||||
|                 if (PublicHolidayInput.LoadValue(rule) !== null) { | ||||
|                     return rule; | ||||
|                 } | ||||
|             } | ||||
|             return ""; | ||||
|         }) | ||||
|         this._phSelector = new PublicHolidayInput(ph); | ||||
| 
 | ||||
|         function update() { | ||||
|             let rules = OH.ToString(rulesFromOhPicker.data); | ||||
|             if (leftoverRules.data.length != 0) { | ||||
|                 rules += ";" + leftoverRules.data.join(";") | ||||
|             } | ||||
|             const phData = ph.data; | ||||
|             if (phData !== undefined && phData !== "") { | ||||
|                 rules += ";" + phData; | ||||
|             } | ||||
|             value.setData(rules); | ||||
|         } | ||||
| 
 | ||||
|         rulesFromOhPicker.addCallback(update); | ||||
|         ph.addCallback(update); | ||||
| 
 | ||||
|         this._leftoverWarning = new VariableUiElement(leftoverRules.map((leftovers: string[]) => { | ||||
| 
 | ||||
|             if (leftovers.length == 0) { | ||||
|                 return ""; | ||||
|             } | ||||
|             return new Combine([ | ||||
|                 Translations.t.general.opening_hours.not_all_rules_parsed, | ||||
|                     new FixedUiElement(leftovers.map(r => `${r}<br/>`).join("")                    ).SetClass("subtle") | ||||
|             ]).Render(); | ||||
| 
 | ||||
|         })) | ||||
| 
 | ||||
|         this._ohPicker = new OpeningHoursPicker(rulesFromOhPicker); | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     GetValue(): UIEventSource<string> { | ||||
|         return this._value; | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return new Combine([ | ||||
|             this._leftoverWarning, | ||||
|             this._ohPicker, | ||||
|             this._phSelector | ||||
|         ]).Render(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
| 
 | ||||
|     IsValid(t: string): boolean { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -7,7 +7,7 @@ import OpeningHoursRange from "./OpeningHoursRange"; | |||
| import Combine from "../../Base/Combine"; | ||||
| 
 | ||||
| export default class OpeningHoursPicker extends InputElement<OpeningHour[]> { | ||||
|     private readonly _ohs: UIEventSource<OpeningHour[]>; | ||||
|     private readonly _ohs: UIEventSource<OpeningHour[]>;     | ||||
|     public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
| 
 | ||||
|     private readonly _backgroundTable: OpeningHoursPickerTable; | ||||
|  | @ -36,8 +36,8 @@ export default class OpeningHoursPicker extends InputElement<OpeningHour[]> { | |||
|                 source.addCallback(_ => { | ||||
|                     self._ohs.setData(OH.MergeTimes(self._ohs.data)) | ||||
|                 }) | ||||
|                 const r = new OpeningHoursRange(source); | ||||
|                 perWeekday[oh.weekday].push(r); | ||||
|                 const r = new OpeningHoursRange(source, `oh-table-${this._backgroundTable.id}`); | ||||
|                 perWeekday[oh.weekday].push(r);  | ||||
|             } | ||||
| 
 | ||||
|             for (let i = 0; i < 7; i++) { | ||||
|  |  | |||
|  | @ -48,18 +48,15 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]> | |||
| 
 | ||||
| 
 | ||||
|             rows += `<tr><td rowspan="2" class="oh-left-col oh-timecell-full">${hs}:00</td>` + | ||||
|                 Utils.Times(weekday => { | ||||
|                     let innerContent = ""; | ||||
|                     if (h == 0) { | ||||
|                         innerContent =  self.weekdays.data[weekday]?.Render() ?? ""; | ||||
|                     } | ||||
|                     return `<td id="${this.id}-timecell-${weekday}-${h}" class="oh-timecell oh-timecell-full"><div class="oh-timecell-inner"></div>${innerContent}</td>`; | ||||
|                 }, 7) + | ||||
|                 '</tr><tr>' + | ||||
|                 Utils.Times(id => `<td id="${this.id}-timecell-${id}-${h}-30" class="oh-timecell oh-timecell-half"><div class="oh-timecell-inner"></div></td>`, 7) + | ||||
|                 Utils.Times(weekday =>  `<td id="${this.id}-timecell-${weekday}-${h}" class="oh-timecell oh-timecell-full"></td>`, 7) + | ||||
|                 '</tr><tr>' +    | ||||
|                 Utils.Times(id => `<td id="${this.id}-timecell-${id}-${h}-30" class="oh-timecell oh-timecell-half"></td>`, 7) + | ||||
|                 '</tr>'; | ||||
|         } | ||||
|         let days = OpeningHoursPickerTable.days.map(day => day.Render()).join("</th><th width='14%'>"); | ||||
|         let days = OpeningHoursPickerTable.days.map((day, i) => { | ||||
|             const innerContent  =  self.weekdays.data[i]?.Render() ?? ""; | ||||
|             return day.Render() + "<span style='width:100%; display:block; position: relative;'>"+innerContent+"</span>"; | ||||
|         }).join("</th><th width='14%'>"); | ||||
|         return `<table id="oh-table-${this.id}" class="oh-table"><tr><th></th><th width='14%'>${days}</th></tr>${rows}</table>`; | ||||
|     } | ||||
| 
 | ||||
|  | @ -181,7 +178,7 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]> | |||
|         for (let i = 1; i < table.rows.length; i++) { | ||||
|             let row = table.rows[i] | ||||
|             for (let j = 0; j < row.cells.length; j++) { | ||||
|                 let cell = row.cells[j].getElementsByClassName("oh-timecell-inner")[0] as HTMLElement | ||||
|                 let cell = row.cells[j] | ||||
|                 let offset = 0; | ||||
|                 if (i % 2 == 1) { | ||||
|                     if (j == 0) { | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import {UIElement} from "../../UIElement"; | ||||
| import {UIEventSource} from "../../../Logic/UIEventSource"; | ||||
| import {OpeningHour} from "../../../Logic/OpeningHours"; | ||||
| import {TextField} from "../TextField"; | ||||
| import {OH, OpeningHour} from "../../../Logic/OpeningHours"; | ||||
| import Combine from "../../Base/Combine"; | ||||
| import {Utils} from "../../../Utils"; | ||||
| import {FixedUiElement} from "../../Base/FixedUiElement"; | ||||
| import {VariableUiElement} from "../../Base/VariableUIElement"; | ||||
| 
 | ||||
| /** | ||||
|  * A single opening hours range, shown on top of the OH-picker table | ||||
|  | @ -12,16 +12,18 @@ import {FixedUiElement} from "../../Base/FixedUiElement"; | |||
| export default class OpeningHoursRange extends UIElement { | ||||
|     private _oh: UIEventSource<OpeningHour>; | ||||
| 
 | ||||
|     private _startTime: TextField; | ||||
|     private _endTime: TextField; | ||||
|     private _deleteRange: UIElement; | ||||
|     private readonly _startTime: UIElement; | ||||
|     private readonly _endTime: UIElement; | ||||
|     private readonly _deleteRange: UIElement; | ||||
|     private readonly _tableId: string; | ||||
| 
 | ||||
|     constructor(oh: UIEventSource<OpeningHour>) { | ||||
|     constructor(oh: UIEventSource<OpeningHour>, tableId: string) { | ||||
|         super(oh); | ||||
|         this._tableId = tableId; | ||||
|         const self = this; | ||||
|         this._oh = oh; | ||||
|         this.SetClass("oh-timerange"); | ||||
|         oh.addCallbackAndRun(oh => { | ||||
|         oh.addCallbackAndRun(() => { | ||||
|             const el = document.getElementById(this.id) as HTMLElement; | ||||
|             self.InnerUpdate(el); | ||||
|         }) | ||||
|  | @ -33,107 +35,15 @@ export default class OpeningHoursRange extends UIElement { | |||
|                 oh.ping(); | ||||
|             }); | ||||
| 
 | ||||
|         this._startTime = new TextField({ | ||||
|             value: oh.map(oh => { | ||||
|                 if (oh) { | ||||
|                     return Utils.TwoDigits(oh.startHour) + ":" + Utils.TwoDigits(oh.startMinutes); | ||||
|                 } | ||||
|             }), | ||||
|             htmlType: "time" | ||||
|         }); | ||||
| 
 | ||||
|         this._endTime = new TextField({ | ||||
|             value: oh.map(oh => { | ||||
|                 if (oh) { | ||||
|                     if (oh.endHour == 24) { | ||||
|                         return "00:00"; | ||||
|                     } | ||||
|                     return Utils.TwoDigits(oh.endHour) + ":" + Utils.TwoDigits(oh.endMinutes); | ||||
|                 } | ||||
|             }), | ||||
|             htmlType: "time" | ||||
|         }); | ||||
|         this._startTime = new VariableUiElement(oh.map(oh => { | ||||
|             return Utils.TwoDigits(oh.startHour) + ":" + Utils.TwoDigits(oh.startMinutes); | ||||
|         })).SetClass("oh-timerange-label") | ||||
| 
 | ||||
|         this._endTime = new VariableUiElement(oh.map(oh => { | ||||
|             return Utils.TwoDigits(oh.endHour) + ":" + Utils.TwoDigits(oh.endMinutes); | ||||
|         })).SetClass("oh-timerange-label") | ||||
| 
 | ||||
|         function applyStartTime() { | ||||
|             if (self._startTime.GetValue().data === undefined) { | ||||
|                 return; | ||||
|             } | ||||
|             const spl = self._startTime.GetValue().data.split(":"); | ||||
|             oh.data.startHour = Number(spl[0]); | ||||
|             oh.data.startMinutes = Number(spl[1]); | ||||
| 
 | ||||
|             if (oh.data.startHour >= oh.data.endHour) { | ||||
|                 if (oh.data.startMinutes + 10 >= oh.data.endMinutes) { | ||||
|                     oh.data.endHour = oh.data.startHour + 1; | ||||
|                     oh.data.endMinutes = oh.data.startMinutes; | ||||
|                     if (oh.data.endHour > 23) { | ||||
|                         oh.data.endHour = 24; | ||||
|                         oh.data.endMinutes = 0; | ||||
|                         oh.data.startHour = Math.min(oh.data.startHour, 23); | ||||
|                         oh.data.startMinutes = Math.min(oh.data.startMinutes, 45); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             oh.ping(); | ||||
|         } | ||||
| 
 | ||||
|         function applyEndTime() { | ||||
|             if (self._endTime.GetValue().data === undefined) { | ||||
|                 return; | ||||
|             } | ||||
|             const spl = self._endTime.GetValue().data.split(":"); | ||||
|             let newEndHour = Number(spl[0]); | ||||
|             const newEndMinutes = Number(spl[1]); | ||||
|             if (newEndHour == 0 && newEndMinutes == 0) { | ||||
|                 newEndHour = 24; | ||||
|             } | ||||
| 
 | ||||
|             if (newEndHour == oh.data.endMinutes && newEndMinutes == oh.data.endMinutes) { | ||||
|                 // NOthing to change
 | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             oh.data.endHour = newEndHour; | ||||
|             oh.data.endMinutes = newEndMinutes; | ||||
| 
 | ||||
|             oh.ping(); | ||||
|         } | ||||
| 
 | ||||
|         this._startTime.GetValue().addCallbackAndRun(startTime => { | ||||
|             const spl = startTime.split(":"); | ||||
|             if (spl[0].startsWith('0') || spl[1].startsWith('0')) { | ||||
|                 return; | ||||
|             } | ||||
|             applyStartTime(); | ||||
|         }); | ||||
| 
 | ||||
|         this._endTime.GetValue().addCallbackAndRun(endTime => { | ||||
|             const spl = endTime.split(":"); | ||||
|             if (spl[0].startsWith('0') || spl[1].startsWith('0')) { | ||||
|                 return; | ||||
|             } | ||||
|             applyEndTime() | ||||
|         }); | ||||
|         this._startTime.enterPressed.addCallback(() => { | ||||
|             applyStartTime(); | ||||
|         }); | ||||
|         this._endTime.enterPressed.addCallbackAndRun(() => { | ||||
|             applyEndTime(); | ||||
|         }) | ||||
| 
 | ||||
|         this._startTime.IsSelected.addCallback(isSelected => { | ||||
|             if (!isSelected) { | ||||
|                 applyStartTime(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         this._endTime.IsSelected.addCallback(isSelected => { | ||||
|             if (!isSelected) { | ||||
|                 applyEndTime(); | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|  | @ -143,8 +53,14 @@ export default class OpeningHoursRange extends UIElement { | |||
|             return ""; | ||||
|         } | ||||
|         const height = this.getHeight(); | ||||
|         return new Combine([this._startTime, this._deleteRange, this._endTime]) | ||||
|             .SetClass(height < 2 ? "oh-timerange-inner-small" : "oh-timerange-inner") | ||||
| 
 | ||||
|         let content = [this._deleteRange] | ||||
|         if (height > 2) { | ||||
|             content = [this._startTime, this._deleteRange, this._endTime]; | ||||
|         } | ||||
| 
 | ||||
|         return new Combine(content) | ||||
|             .SetClass("oh-timerange-inner") | ||||
|             .Render(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -167,10 +83,19 @@ export default class OpeningHoursRange extends UIElement { | |||
|         if (oh === undefined) { | ||||
|             return; | ||||
|         } | ||||
|         const height = this.getHeight(); | ||||
|         el.style.height = `${height * 200}%` | ||||
|         const upperDiff = (oh.startHour + oh.startMinutes / 60); | ||||
|         el.style.marginTop = `${2 * upperDiff * el.parentElement.offsetHeight - upperDiff*0.75}px`; | ||||
| 
 | ||||
|         // The header cell containing monday, tuesday, ...
 | ||||
|         const table = document.getElementById(this._tableId) as HTMLTableElement; | ||||
| 
 | ||||
|         const bodyRect = document.body.getBoundingClientRect(); | ||||
|         const rangeStart = table.rows[1].cells[1].getBoundingClientRect().top - bodyRect.top; | ||||
|         const rangeEnd = table.rows[table.rows.length - 1].cells[1].getBoundingClientRect().bottom - bodyRect.top; | ||||
| 
 | ||||
|         const pixelsPerHour = (rangeEnd - rangeStart) / 24; | ||||
| 
 | ||||
|         el.style.top = (pixelsPerHour * OH.startTime(oh)) + "px"; | ||||
|         el.style.height = (pixelsPerHour * (OH.endTime(oh) - OH.startTime(oh))) + "px"; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										163
									
								
								UI/Input/OpeningHours/PublicHolidayInput.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								UI/Input/OpeningHours/PublicHolidayInput.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,163 @@ | |||
| import {InputElement} from "../InputElement"; | ||||
| import {UIEventSource} from "../../../Logic/UIEventSource"; | ||||
| import {UIElement} from "../../UIElement"; | ||||
| import {DropDown} from "../DropDown"; | ||||
| import Translations from "../../i18n/Translations"; | ||||
| import Combine from "../../Base/Combine"; | ||||
| import {TextField} from "../TextField"; | ||||
| import {OH} from "../../../Logic/OpeningHours"; | ||||
| 
 | ||||
| export default class PublicHolidayInput extends InputElement<string> { | ||||
|     IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
| 
 | ||||
|     private readonly _value: UIEventSource<string>; | ||||
|     private readonly _dropdown: UIElement; | ||||
|     private readonly _mode: UIEventSource<string>; | ||||
|     private readonly _startHour: UIElement; | ||||
|     private readonly _endHour: UIElement; | ||||
| 
 | ||||
|     constructor(value: UIEventSource<string> = new UIEventSource<string>("")) { | ||||
|         super(); | ||||
|         this._value = value; | ||||
| 
 | ||||
|         const dropdown = new DropDown( | ||||
|             Translations.t.general.opening_hours.open_during_ph, | ||||
|             [ | ||||
|                 {shown: "unknown", value: ""}, | ||||
|                 {shown: "closed", value: "off"}, | ||||
|                 {shown: "opened", value: " "} | ||||
|             ] | ||||
|         ); | ||||
|         this._dropdown = dropdown.SetStyle("display:inline-block;"); | ||||
|         this._mode = dropdown.GetValue(); | ||||
|         this.ListenTo(dropdown.GetValue()); | ||||
| 
 | ||||
|         const start = new TextField({ | ||||
|             placeholder: "starthour", | ||||
|             htmlType: "time" | ||||
|         }); | ||||
|         const end = new TextField({ | ||||
|             placeholder: "starthour", | ||||
|             htmlType: "time" | ||||
|         }); | ||||
|         this._startHour = start.SetStyle("display:inline-block;"); | ||||
|         this._endHour = end.SetStyle("display:inline-block;"); | ||||
|         const self = this; | ||||
| 
 | ||||
|         this._value.addCallbackAndRun(ph => { | ||||
|             if (ph === undefined) { | ||||
|                 return; | ||||
|             } | ||||
|             const parsed = PublicHolidayInput.LoadValue(ph); | ||||
|             if (parsed === null) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             dropdown.GetValue().setData(parsed.mode); | ||||
|             if (parsed.start) { | ||||
|                 start.GetValue().setData(parsed.start); | ||||
|             } | ||||
|             if (parsed.end) { | ||||
|                 end.GetValue().setData(parsed.end); | ||||
|             } | ||||
| 
 | ||||
|         }) | ||||
| 
 | ||||
| 
 | ||||
|         function updateValue() { | ||||
|             const phStart = dropdown.GetValue().data; | ||||
|             if (phStart === undefined || phStart === "") { | ||||
|                 // Unknown
 | ||||
|                 self._value.setData(""); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if (phStart === " ") { | ||||
|                 // THey are open, we need to include the start- and enddate
 | ||||
|                 const startV = start.GetValue().data; | ||||
|                 const endV = end.GetValue().data; | ||||
|                 if (startV === undefined || endV === undefined) { | ||||
|                     self._value.setData(`PH open`); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 self._value.setData(`PH ${startV}-${endV}`); | ||||
|                 return; | ||||
|             } | ||||
|             self._value.setData(`PH ${phStart}`); | ||||
|         } | ||||
| 
 | ||||
|         dropdown.GetValue().addCallbackAndRun(() => { | ||||
|             updateValue(); | ||||
|         }); | ||||
|         start.GetValue().addCallbackAndRun(() => { | ||||
|             updateValue(); | ||||
|         }); | ||||
|         end.GetValue().addCallbackAndRun(() => { | ||||
|             updateValue(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public static LoadValue(str: string): { | ||||
|         mode: string, | ||||
|         start?: string, | ||||
|         end?: string | ||||
|     } { | ||||
|         str = str.trim(); | ||||
|         if (!str.startsWith("PH")) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         str = str.trim(); | ||||
|         if (str === "PH off") { | ||||
|             return { | ||||
|                 mode: "off" | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (!str.startsWith("PH ")) { | ||||
|             return null; | ||||
|         } | ||||
|         try { | ||||
| 
 | ||||
|             const timerange = OH.parseHHMMRange(str.substring(2)); | ||||
|             if (timerange === null) { | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             return { | ||||
|                 mode: " ", | ||||
|                 start: OH.hhmm(timerange.startHour, timerange.startMinutes), | ||||
|                 end: OH.hhmm(timerange.endHour, timerange.endMinutes), | ||||
| 
 | ||||
|             } | ||||
|         } catch (e) { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         const mode = this._mode.data; | ||||
|         if (mode === " ") { | ||||
|             return new Combine([this._dropdown, | ||||
|                 " ", | ||||
|                 Translations.t.general.opening_hours.opensAt, | ||||
|                 " ", | ||||
|                 this._startHour, | ||||
|                 " ", | ||||
|                 Translations.t.general.opening_hours.openTill, | ||||
|                 " ", | ||||
|                 this._endHour]).Render(); | ||||
|         } | ||||
|         return this._dropdown.Render(); | ||||
|     } | ||||
| 
 | ||||
|     GetValue(): UIEventSource<string> { | ||||
|         return this._value; | ||||
|     } | ||||
| 
 | ||||
|     IsValid(t: string): boolean { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -2,6 +2,7 @@ import {UIElement} from "../UIElement"; | |||
| import {InputElement} from "./InputElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Combine from "../Base/Combine"; | ||||
| 
 | ||||
| export class TextField extends InputElement<string> { | ||||
|     private readonly value: UIEventSource<string>; | ||||
|  | @ -12,12 +13,14 @@ export class TextField extends InputElement<string> { | |||
|     private readonly _textAreaRows: number; | ||||
| 
 | ||||
|     private readonly _isValid: (string, country) => boolean; | ||||
|     private _label: UIElement; | ||||
| 
 | ||||
|     constructor(options?: { | ||||
|         placeholder?: string | UIElement, | ||||
|         value?: UIEventSource<string>, | ||||
|         textArea?: boolean, | ||||
|         htmlType?: string, | ||||
|         label?: UIElement, | ||||
|         textAreaRows?: number, | ||||
|         isValid?: ((s: string, country?: string) => boolean) | ||||
|     }) { | ||||
|  | @ -28,6 +31,7 @@ export class TextField extends InputElement<string> { | |||
|         this._htmlType = options.textArea ? "area" : (options.htmlType ?? "text"); | ||||
|         this.value = options?.value ?? new UIEventSource<string>(undefined); | ||||
| 
 | ||||
|         this._label = options.label; | ||||
|         this._textAreaRows = options.textAreaRows; | ||||
|         this._isValid = options.isValid ?? ((str, country) => true); | ||||
| 
 | ||||
|  | @ -64,10 +68,18 @@ export class TextField extends InputElement<string> { | |||
|         } | ||||
| 
 | ||||
|         const placeholder = this._placeholder.InnerRender().replace("'", "'"); | ||||
| 
 | ||||
|         return `<div id="${this.id}"><form onSubmit='return false' class='form-text-field'>` + | ||||
|             `<input type='${this._htmlType}' placeholder='${placeholder}' id='txt-${this.id}'/>` + | ||||
|             `</form></div>`; | ||||
|         let label = ""; | ||||
|         if (this._label != undefined) { | ||||
|             label = this._label.Render(); | ||||
|         } | ||||
|         return new Combine([ | ||||
|             `<div id="${this.id}">`, | ||||
|             `<form onSubmit='return false' class='form-text-field'>`, | ||||
|             label, | ||||
|             `<input type='${this._htmlType}' placeholder='${placeholder}' id='txt-${this.id}'/>`, | ||||
|             `</form>`, | ||||
|             `</div>` | ||||
|         ]).Render(); | ||||
|     } | ||||
|      | ||||
|     InnerUpdate() { | ||||
|  |  | |||
|  | @ -8,8 +8,7 @@ import {UIElement} from "../UIElement"; | |||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import CombinedInputElement from "./CombinedInputElement"; | ||||
| import SimpleDatePicker from "./SimpleDatePicker"; | ||||
| import OpeningHoursPicker from "./OpeningHours/OpeningHoursPicker"; | ||||
| import {OpeningHour, OH} from "../../Logic/OpeningHours"; | ||||
| import OpeningHoursInput from "./OpeningHours/OpeningHoursInput"; | ||||
| 
 | ||||
| interface TextFieldDef { | ||||
|     name: string, | ||||
|  | @ -150,18 +149,7 @@ export default class ValidatedTextField { | |||
|             (s, country) => true, // TODO
 | ||||
|             str => str,  | ||||
|             (value) => { | ||||
|                  | ||||
|                 const sourceMapped = value.map(OH.Parse, [], OH.ToString); | ||||
|                  | ||||
|                 const input = new InputElementMap<OpeningHour[], string>(new OpeningHoursPicker(sourceMapped), | ||||
|                     (a, b) => a === b, | ||||
|                     ohs =>  OH.ToString(ohs), | ||||
|                     str => OH.Parse(str) | ||||
|                 ) | ||||
|                 input.GetValue().addCallback(latest => { | ||||
|                     value.setData(latest); | ||||
|                 }) | ||||
|                 return input; | ||||
|                 return new OpeningHoursInput(value); | ||||
|             } | ||||
|         ) | ||||
|     ] | ||||
|  |  | |||
|  | @ -1,38 +1,71 @@ | |||
| import {UIElement} from "./UIElement"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import * as opening_hours from "opening_hours"; | ||||
| import opening_hours from "opening_hours"; | ||||
| 
 | ||||
| export default class OhVisualization extends UIElement { | ||||
| export default class OpeningHoursVisualization extends UIElement { | ||||
| 
 | ||||
|     constructor(openingHours: UIEventSource<any>) { | ||||
|         super(openingHours); | ||||
|     constructor(tags: UIEventSource<any>) { | ||||
|         super(tags); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private static GetRanges(tags: any, from: Date, to: Date): { | ||||
|         isOpen: boolean, | ||||
|         isUnknown: boolean, | ||||
|         comment: string, | ||||
|         startDate: Date | ||||
|     }[] { | ||||
| 
 | ||||
|         const oh = new opening_hours(tags.opening_hours, { | ||||
|             lat: tags._lat, | ||||
|             lon: tags._lon, | ||||
|             address: { | ||||
|                 country_code: tags._country | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         const values = []; | ||||
| 
 | ||||
|         const iterator = oh.getIterator(from); | ||||
| 
 | ||||
|         while (iterator.advance(to)) { | ||||
| 
 | ||||
|             const value = { | ||||
|                 isUnknown: iterator.getUnknown(), | ||||
|                 isOpen: iterator.getState(), | ||||
|                 comment: iterator.getComment(), | ||||
|                 startDate: iterator.getDate() | ||||
|             } | ||||
| 
 | ||||
|             if (value.comment === undefined && !value.isOpen && !value.isUnknown) { | ||||
|                 // simply closed, nothing special here
 | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             console.log(value) | ||||
|             values.push(value); | ||||
| 
 | ||||
| 
 | ||||
|         } | ||||
|         return values; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
| 
 | ||||
|         const oh = new opening_hours(this._source.data, {}); | ||||
| 
 | ||||
|        let nominatim_example =  [{ | ||||
|             "place_id": 79276782, | ||||
|             "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright", | ||||
|             "osm_type": "way", | ||||
|             "osm_id": 4575088, | ||||
|             "boundingbox": ["52.5519288", "52.5541724", "-1.8278941", "-1.8238916"], | ||||
|             "lat": "52.553624", | ||||
|             "lon": "-1.8256057", | ||||
|             "display_name": "Pilkington Avenue, Sutton Coldfield, Birmingham, West Midlands Combined Authority, England, B72, United Kingdom", | ||||
|             "place_rank": 26, | ||||
|             "category": "highway", | ||||
|             "type": "residential", | ||||
|             "importance": 0.4, | ||||
|             "geojson": { | ||||
|                 "type": "LineString", | ||||
|                 "coordinates": [[-1.8278941, 52.55417], [-1.8277256, 52.5541716], [-1.8276423, 52.5541724], [-1.8267652, 52.5539852], [-1.8261462, 52.5538445], [-1.8258137, 52.5537286], [-1.8256057, 52.553624], [-1.8254024, 52.5534973], [-1.8252343, 52.5533435], [-1.8245486, 52.5526243], [-1.8238916, 52.5519288]] | ||||
|             } | ||||
|         }] | ||||
|         const from = new Date("2019-12-31"); | ||||
|         const to = new Date("2020-01-05"); | ||||
| 
 | ||||
|         const ranges = OpeningHoursVisualization.GetRanges(this._source.data, from, to); | ||||
| 
 | ||||
| 
 | ||||
|         return ""; | ||||
|         let text = ""; | ||||
|         for (const range of ranges) { | ||||
|             text += `From${range.startDate} it is${range.isOpen} ${range.comment?? ""}<br/>` | ||||
|         } | ||||
| 
 | ||||
|         return text; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -101,7 +101,7 @@ export abstract class UIElement extends UIEventSource<string> { | |||
|             const self = this; | ||||
|             element.onclick = (e) => { | ||||
|                 // @ts-ignore
 | ||||
|                 if (e.consumed) { | ||||
|                 if(e.consumed){ | ||||
|                     return; | ||||
|                 } | ||||
|                 self._onClick(); | ||||
|  |  | |||
|  | @ -881,6 +881,24 @@ export default class Translations { | |||
|                     "nl": "Zondag", | ||||
|                     "fr": "Dimance", | ||||
|                 }) | ||||
|             }, | ||||
|             opening_hours: { | ||||
|                 open_during_ph: new T({ | ||||
|                     "nl": "Op een feestdag is deze zaak", | ||||
|                     "en":"During a public holiday, this amenity is" | ||||
|                 }), | ||||
|                 opensAt: new T({ | ||||
|                     "en":"from", | ||||
|                     "nl":"vanaf" | ||||
|                 }),openTill: new T({ | ||||
|                     "en":"till", | ||||
|                     "nl":"tot" | ||||
|                 }), | ||||
|                 not_all_rules_parsed: new T({ | ||||
|                     "en":"The openin hours of this shop are complicated. The following rules are ignored in the input element:" | ||||
|                 }) | ||||
| 
 | ||||
| 
 | ||||
|             } | ||||
|         }, | ||||
|         favourite: { | ||||
|  |  | |||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -15,14 +15,14 @@ | |||
|     vertical-align: top; | ||||
| } | ||||
| 
 | ||||
| .oh-timecell-inner:hover { | ||||
|     background-color: #ffd1be; | ||||
| .oh-timecell:hover { | ||||
|     background-color: #ffd1be !important; | ||||
| } | ||||
| 
 | ||||
| .oh-timecell { | ||||
|     background-color: white; | ||||
|     border-left: 1px solid #eee; | ||||
|     border-right: 1px solid #eee; | ||||
|     border-right: 1px solid #ccc; | ||||
|     position: relative; | ||||
|     box-sizing: border-box; | ||||
| } | ||||
|  | @ -31,27 +31,19 @@ | |||
|     background-color: orange; | ||||
| } | ||||
| 
 | ||||
| .oh-timecell-half  .oh-timecell-inner{ | ||||
|     border-top: 0.5px solid #eee | ||||
| .oh-timecell-half{ | ||||
|     background-color: aliceblue; | ||||
| } | ||||
| 
 | ||||
| .oh-timecell-half.oh-timecell-selected  .oh-timecell-inner { | ||||
|     border-top: 0.5px solid lightsalmon; | ||||
| } | ||||
| 
 | ||||
| .oh-timecell-full  .oh-timecell-inner{ | ||||
|     border-top: 1px solid #ccc | ||||
| } | ||||
| 
 | ||||
| .oh-timecell-full.oh-timecell-selected .oh-timecell-inner { | ||||
|     border-top: 1px solid lightsalmon; | ||||
| .oh-timecell-half.oh-timecell-selected { | ||||
|    background-color:  lightsalmon; | ||||
| } | ||||
| 
 | ||||
| .oh-left-col { | ||||
|     border-top: 1px solid #aaa; | ||||
|     border-bottom: 1px solid #aaa; | ||||
|     margin: 0; | ||||
|     width: 0.5em; | ||||
|     font-size: large; | ||||
|     margin: 0; | ||||
|     padding: 0; | ||||
|     padding-right: 0.2em; | ||||
|     background: #ddd; | ||||
|  | @ -62,14 +54,6 @@ | |||
|     height: 0.5em; | ||||
| } | ||||
| 
 | ||||
| .oh-timecell-inner { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     margin: 0; | ||||
|     padding: 0; | ||||
|     position: absolute; | ||||
| } | ||||
| 
 | ||||
| .oh-timerange { | ||||
|     border-radius: 0.5em; | ||||
|     margin: 2px; | ||||
|  | @ -119,4 +103,8 @@ | |||
| .oh-delete-range img { | ||||
|     height: 100%; | ||||
|     max-width: 2em; | ||||
| } | ||||
| 
 | ||||
| .oh-timerange-label{ | ||||
|     color: white; | ||||
| } | ||||
							
								
								
									
										26
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -1,23 +1,17 @@ | |||
| //*
 | ||||
| import OpeningHoursPicker from "./UI/Input/OpeningHours/OpeningHoursPicker"; | ||||
| import {VariableUiElement} from "./UI/Base/VariableUIElement"; | ||||
| import {OH} from "./Logic/OpeningHours"; | ||||
| 
 | ||||
| const picker = new OpeningHoursPicker(); | ||||
| new VariableUiElement(picker.GetValue().map(OH.ToString)).AttachTo("extradiv"); | ||||
| picker.AttachTo("maindiv"); | ||||
| import OpeningHoursVisualization from "./UI/OhVisualization"; | ||||
| import {UIEventSource} from "./Logic/UIEventSource"; | ||||
| 
 | ||||
| new OpeningHoursVisualization(new UIEventSource<any>({ | ||||
|         opening_hours: "mo-fr 09:00-17:00; Sa 09:00-17:00 'by appointment'; PH off; Th[1] off;", | ||||
|         _country: "be", | ||||
|         _lat: "51.2", | ||||
|         _lon: "3.2" | ||||
|     } | ||||
| )).AttachTo("maindiv") | ||||
| 
 | ||||
| 
 | ||||
| window.setTimeout(() => { | ||||
| picker.GetValue().setData([{ | ||||
|     weekday: 1, | ||||
|     startHour: 11, | ||||
|     startMinutes: 0, | ||||
|     endHour: 17, | ||||
|     endMinutes: 0 | ||||
| }]); | ||||
|      | ||||
| }, 1000) | ||||
| /*/ | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ import Translations from "../UI/i18n/Translations"; | |||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import {TagRendering} from "../UI/TagRendering"; | ||||
| import {OH, OpeningHour} from "../Logic/OpeningHours"; | ||||
| import PublicHolidayInput from "../UI/Input/OpeningHours/PublicHolidayInput"; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -218,7 +219,7 @@ new T([ | |||
|             }, | ||||
| 
 | ||||
|         ]); | ||||
|         equal(rules, "Mo-Tu 10:00-12:00, 13:00-17:00;"); | ||||
|         equal(rules, "Mo-Tu 10:00-12:00, 13:00-17:00"); | ||||
|     }], | ||||
|     ["JOIN OH 2",() => { | ||||
|         const rules = OH.ToString([ | ||||
|  | @ -238,7 +239,7 @@ new T([ | |||
|             }, | ||||
| 
 | ||||
|         ]); | ||||
|         equal(rules, "Tu 10:00-12:00, 13:00-17:00;"); | ||||
|         equal(rules, "Tu 10:00-12:00, 13:00-17:00"); | ||||
|     }], | ||||
|     ["JOIN OH 3",() => { | ||||
|         const rules = OH.ToString([ | ||||
|  | @ -258,7 +259,7 @@ new T([ | |||
|             }, | ||||
| 
 | ||||
|         ]); | ||||
|         equal(rules, "Tu 10:00-12:00; Th 13:00-17:00;"); | ||||
|         equal(rules, "Tu 10:00-12:00; Th 13:00-17:00"); | ||||
|     }], | ||||
|     ["JOIN OH 3",() => { | ||||
|         const rules = OH.ToString([ | ||||
|  | @ -278,7 +279,7 @@ new T([ | |||
|             }, | ||||
| 
 | ||||
|         ]); | ||||
|         equal(rules, "Tu 10:00-12:00; Su 13:00-17:00;"); | ||||
|         equal(rules, "Tu 10:00-12:00; Su 13:00-17:00"); | ||||
|     }], | ||||
|     ["OH 24/7",() => { | ||||
|         const rules = OH.Parse("24/7"); | ||||
|  | @ -286,5 +287,17 @@ new T([ | |||
|         equal(rules[0].startHour, 0); | ||||
|         const asStr = OH.ToString(rules); | ||||
|         equal(asStr, "24/7"); | ||||
|     }], | ||||
|     ["OH Th[-1] off",() => { | ||||
|         const rules = OH.ParseRule("Th[-1] off"); | ||||
|         equal(rules, null); | ||||
|     }], | ||||
|     ["OHNo parsePH 12:00-17:00",() => { | ||||
|         const rules = OH.ParseRule("PH 12:00-17:00"); | ||||
|         equal(rules, null); | ||||
|     }], | ||||
|     ["OH Parse PH 12:00-17:00",() => { | ||||
|         const rules = PublicHolidayInput.LoadValue("PH 12:00-17:00"); | ||||
|         equal(rules.mode, " "); | ||||
|     }] | ||||
| ]); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue