forked from MapComplete/MapComplete
		
	Last fixes to the OH picker
This commit is contained in:
		
							parent
							
								
									64ec06bfc8
								
							
						
					
					
						commit
						48f66bd17e
					
				
					 11 changed files with 327 additions and 311 deletions
				
			
		| 
						 | 
				
			
			@ -210,6 +210,7 @@ export class InitUiElements {
 | 
			
		|||
 | 
			
		||||
        // Reset the loading message once things are loaded
 | 
			
		||||
        new CenterMessageBox().AttachTo("centermessage");
 | 
			
		||||
        document.getElementById("centermessage").classList.add("pointer-events-none")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,9 +50,9 @@ export class OH {
 | 
			
		|||
        let rangeStart = 0;
 | 
			
		||||
        let rangeEnd = 0;
 | 
			
		||||
 | 
			
		||||
        function pushRule(){
 | 
			
		||||
        function pushRule() {
 | 
			
		||||
            const rule = stringPerWeekday[rangeStart];
 | 
			
		||||
            if(rule === ""){
 | 
			
		||||
            if (rule === "") {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (rangeStart == (rangeEnd - 1)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +61,7 @@ export class OH {
 | 
			
		|||
                );
 | 
			
		||||
            } else {
 | 
			
		||||
                rules.push(
 | 
			
		||||
                    `${OH.days[rangeStart]}-${OH.days[rangeEnd-1]} ${rule}`
 | 
			
		||||
                    `${OH.days[rangeStart]}-${OH.days[rangeEnd - 1]} ${rule}`
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -90,13 +90,22 @@ export class OH {
 | 
			
		|||
     * @constructor
 | 
			
		||||
     */
 | 
			
		||||
    public static MergeTimes(ohs: OpeningHour[]): OpeningHour[] {
 | 
			
		||||
        const queue = [...ohs];
 | 
			
		||||
        const queue = ohs.map(oh => {
 | 
			
		||||
            if (oh.endHour === 0 && oh.endMinutes === 0) {
 | 
			
		||||
                const newOh = {
 | 
			
		||||
                        ...oh
 | 
			
		||||
                }
 | 
			
		||||
                newOh.endHour = 24
 | 
			
		||||
                return newOh
 | 
			
		||||
            }
 | 
			
		||||
            return oh;
 | 
			
		||||
        });
 | 
			
		||||
        const newList = [];
 | 
			
		||||
        while (queue.length > 0) {
 | 
			
		||||
            let maybeAdd = queue.pop();
 | 
			
		||||
 | 
			
		||||
            let doAddEntry = true;
 | 
			
		||||
            if(maybeAdd.weekday == undefined){
 | 
			
		||||
            if (maybeAdd.weekday == undefined) {
 | 
			
		||||
                doAddEntry = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -140,8 +149,8 @@ export class OH {
 | 
			
		|||
                    queue.push({
 | 
			
		||||
                        startHour: startHour,
 | 
			
		||||
                        startMinutes: startMinutes,
 | 
			
		||||
                        endHour:endHour,
 | 
			
		||||
                        endMinutes:endMinutes,
 | 
			
		||||
                        endHour: endHour,
 | 
			
		||||
                        endMinutes: endMinutes,
 | 
			
		||||
                        weekday: guard.weekday
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -190,21 +199,6 @@ export class OH {
 | 
			
		|||
            OH.endTime(checked) <= OH.endTime(mightLieIn)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
        const hm = {hours: Number(spl[0].trim()), minutes: Number(spl[1].trim())};
 | 
			
		||||
        if(isNaN(hm.hours) || isNaN(hm.minutes) ){
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return hm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static parseHHMMRange(hhmmhhmm: string): {
 | 
			
		||||
        startHour: number,
 | 
			
		||||
        startMinutes: number,
 | 
			
		||||
| 
						 | 
				
			
			@ -226,80 +220,6 @@ export class OH {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ParseHhmmRanges(hhmms: string): {
 | 
			
		||||
        startHour: number,
 | 
			
		||||
        startMinutes: number,
 | 
			
		||||
        endHour: number,
 | 
			
		||||
        endMinutes: number
 | 
			
		||||
    }[] {
 | 
			
		||||
        if (hhmms === "off") {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
        return hhmms.split(",")
 | 
			
		||||
            .map(s => s.trim())
 | 
			
		||||
            .filter(str => str !== "")
 | 
			
		||||
            .map(OH.parseHHMMRange)
 | 
			
		||||
            .filter(v => v != null)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ParseWeekday(weekday: string): number {
 | 
			
		||||
        return OH.daysIndexed[weekday.trim().toLowerCase()];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ParseWeekdayRange(weekdays: string): number[] {
 | 
			
		||||
        const split = weekdays.split("-");
 | 
			
		||||
        if (split.length == 1) {
 | 
			
		||||
            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 {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ParseWeekdayRanges(weekdays: string): number[] {
 | 
			
		||||
        let ranges = [];
 | 
			
		||||
        let split = weekdays.split(",");
 | 
			
		||||
        for (const weekday of split) {
 | 
			
		||||
            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) {
 | 
			
		||||
                ohs.push({
 | 
			
		||||
                    weekday: weekday,
 | 
			
		||||
                    startHour: timerange.startHour, startMinutes: timerange.startMinutes,
 | 
			
		||||
                    endHour: timerange.endHour, endMinutes: timerange.endMinutes,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return ohs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ParseRule(rule: string): OpeningHour[] {
 | 
			
		||||
        try {
 | 
			
		||||
            if (rule.trim() == "24/7") {
 | 
			
		||||
| 
						 | 
				
			
			@ -331,6 +251,49 @@ export class OH {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ParsePHRule(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 === "PH open") {
 | 
			
		||||
            return {
 | 
			
		||||
                mode: "open"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Parse(rules: string) {
 | 
			
		||||
        if (rules === undefined || rules === "") {
 | 
			
		||||
| 
						 | 
				
			
			@ -342,7 +305,7 @@ export class OH {
 | 
			
		|||
        const split = rules.split(";");
 | 
			
		||||
 | 
			
		||||
        for (const rule of split) {
 | 
			
		||||
            if(rule === ""){
 | 
			
		||||
            if (rule === "") {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            try {
 | 
			
		||||
| 
						 | 
				
			
			@ -477,5 +440,94 @@ export class OH {
 | 
			
		|||
        return values;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
        const hm = {hours: Number(spl[0].trim()), minutes: Number(spl[1].trim())};
 | 
			
		||||
        if (isNaN(hm.hours) || isNaN(hm.minutes)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return hm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ParseHhmmRanges(hhmms: string): {
 | 
			
		||||
        startHour: number,
 | 
			
		||||
        startMinutes: number,
 | 
			
		||||
        endHour: number,
 | 
			
		||||
        endMinutes: number
 | 
			
		||||
    }[] {
 | 
			
		||||
        if (hhmms === "off") {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
        return hhmms.split(",")
 | 
			
		||||
            .map(s => s.trim())
 | 
			
		||||
            .filter(str => str !== "")
 | 
			
		||||
            .map(OH.parseHHMMRange)
 | 
			
		||||
            .filter(v => v != null)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ParseWeekday(weekday: string): number {
 | 
			
		||||
        return OH.daysIndexed[weekday.trim().toLowerCase()];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ParseWeekdayRange(weekdays: string): number[] {
 | 
			
		||||
        const split = weekdays.split("-");
 | 
			
		||||
        if (split.length == 1) {
 | 
			
		||||
            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 {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ParseWeekdayRanges(weekdays: string): number[] {
 | 
			
		||||
        let ranges = [];
 | 
			
		||||
        let split = weekdays.split(",");
 | 
			
		||||
        for (const weekday of split) {
 | 
			
		||||
            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) {
 | 
			
		||||
                ohs.push({
 | 
			
		||||
                    weekday: weekday,
 | 
			
		||||
                    startHour: timerange.startHour, startMinutes: timerange.startMinutes,
 | 
			
		||||
                    endHour: timerange.endHour, endMinutes: timerange.endMinutes,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return ohs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,14 +37,14 @@ export default class OpeningHoursInput extends InputElement<string> {
 | 
			
		|||
                if (OH.ParseRule(rule) !== null) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                if (PublicHolidayInput.LoadValue(rule) !== null) {
 | 
			
		||||
                if (OH.ParsePHRule(rule) !== null) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                leftOvers.push(rule);
 | 
			
		||||
            }
 | 
			
		||||
            return leftOvers;
 | 
			
		||||
        })
 | 
			
		||||
        // NOte: MUST be bound AFTER the leftover rules!
 | 
			
		||||
        // Note: MUST be bound AFTER the leftover rules!
 | 
			
		||||
        const rulesFromOhPicker = value.map(OH.Parse);
 | 
			
		||||
 | 
			
		||||
        const ph = value.map<string>(str => {
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +53,7 @@ export default class OpeningHoursInput extends InputElement<string> {
 | 
			
		|||
            }
 | 
			
		||||
            const rules = str.split(";");
 | 
			
		||||
            for (const rule of rules) {
 | 
			
		||||
                if (PublicHolidayInput.LoadValue(rule) !== null) {
 | 
			
		||||
                if (OH.ParsePHRule(rule) !== null) {
 | 
			
		||||
                    return rule;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,4 @@
 | 
			
		|||
import {UIEventSource} from "../../Logic/UIEventSource";
 | 
			
		||||
import OpeningHoursRange from "./OpeningHoursRange";
 | 
			
		||||
import Combine from "../Base/Combine";
 | 
			
		||||
import OpeningHoursPickerTable from "./OpeningHoursPickerTable";
 | 
			
		||||
import {OH, OpeningHour} from "./OpeningHours";
 | 
			
		||||
import {InputElement} from "../Input/InputElement";
 | 
			
		||||
| 
						 | 
				
			
			@ -22,8 +20,6 @@ export default class OpeningHoursPicker extends InputElement<OpeningHour[]> {
 | 
			
		|||
 | 
			
		||||
        this._backgroundTable = new OpeningHoursPickerTable(this._ohs);
 | 
			
		||||
        this._backgroundTable.ConstructElement()
 | 
			
		||||
 | 
			
		||||
        ohs.ping();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    InnerRender(): BaseUIElement {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,16 +52,22 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
 | 
			
		|||
        const table = document.createElement("table")
 | 
			
		||||
        table.classList.add("oh-table")
 | 
			
		||||
        
 | 
			
		||||
        const cellHeightInPx = 14;
 | 
			
		||||
 | 
			
		||||
        const headerRow = document.createElement("tr")
 | 
			
		||||
        headerRow.appendChild(document.createElement("th"))
 | 
			
		||||
        headerRow.classList.add("relative")
 | 
			
		||||
        for (let i = 0; i < OpeningHoursPickerTable.days.length; i++) {
 | 
			
		||||
            let weekday = OpeningHoursPickerTable.days[i].Clone();
 | 
			
		||||
            const cell = document.createElement("th")
 | 
			
		||||
            cell.style.width = "14%"
 | 
			
		||||
            cell.appendChild(weekday.ConstructElement())
 | 
			
		||||
            
 | 
			
		||||
            const fullColumnSpan = this.weekdayElements[i]
 | 
			
		||||
            fullColumnSpan.classList.add("w-full","h-full","relative")
 | 
			
		||||
            fullColumnSpan.style.height = "42rem"  
 | 
			
		||||
            fullColumnSpan.classList.add("w-full","relative")
 | 
			
		||||
            
 | 
			
		||||
            // We need to round! The table height is rounded as following, we use this to calculate the actual number of pixels afterwards
 | 
			
		||||
            fullColumnSpan.style.height = ( (cellHeightInPx) * 48) + "px"  
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
            const ranges = new VariableUiElement(
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +104,7 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
 | 
			
		|||
            const hs = Utils.TwoDigits(h);
 | 
			
		||||
            const firstCell = document.createElement("td")
 | 
			
		||||
            firstCell.rowSpan = 2
 | 
			
		||||
            firstCell.classList.add("oh-left-col", "oh-timecell-full", "border-box","h-2")
 | 
			
		||||
            firstCell.classList.add("oh-left-col", "oh-timecell-full", "border-box")
 | 
			
		||||
            firstCell.appendChild(new FixedUiElement(hs + ":00").ConstructElement())
 | 
			
		||||
 | 
			
		||||
            const evenRow = document.createElement("tr")
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +115,9 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
 | 
			
		|||
                cell.classList.add("oh-timecell", "oh-timecell-full", `oh-timecell-${weekday}`)
 | 
			
		||||
                evenRow.appendChild(cell)
 | 
			
		||||
            }
 | 
			
		||||
            evenRow.style.height = (cellHeightInPx)+"px";
 | 
			
		||||
            evenRow.style.maxHeight = evenRow.style.height;
 | 
			
		||||
            evenRow.style.minHeight = evenRow.style.height;
 | 
			
		||||
            table.appendChild(evenRow)
 | 
			
		||||
 | 
			
		||||
            const oddRow = document.createElement("tr")
 | 
			
		||||
| 
						 | 
				
			
			@ -118,6 +127,9 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
 | 
			
		|||
                cell.classList.add("oh-timecell", "oh-timecell-half", `oh-timecell-${weekday}`)
 | 
			
		||||
                oddRow.appendChild(cell)
 | 
			
		||||
            }
 | 
			
		||||
            oddRow.style.minHeight = evenRow.style.height;
 | 
			
		||||
            oddRow.style.maxHeight = evenRow.style.height;
 | 
			
		||||
 | 
			
		||||
            table.appendChild(oddRow)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,28 +24,28 @@ export default class OpeningHoursRange extends BaseUIElement {
 | 
			
		|||
    InnerConstructElement(): HTMLElement {
 | 
			
		||||
        const height = this.getHeight();
 | 
			
		||||
        const oh = this._oh;
 | 
			
		||||
        const startTime = new FixedUiElement(Utils.TwoDigits(oh.startHour) + ":" + Utils.TwoDigits(oh.startMinutes)).SetClass("oh-timerange-label")
 | 
			
		||||
        const endTime = new FixedUiElement(Utils.TwoDigits(oh.endHour) + ":" + Utils.TwoDigits(oh.endMinutes)).SetClass("oh-timerange-label")
 | 
			
		||||
 | 
			
		||||
        const startTime = new FixedUiElement(Utils.TwoDigits(oh.startHour) + ":" + Utils.TwoDigits(oh.startMinutes))
 | 
			
		||||
        const endTime = new FixedUiElement(Utils.TwoDigits(oh.endHour) + ":" + Utils.TwoDigits(oh.endMinutes))
 | 
			
		||||
 | 
			
		||||
        const deleteRange =
 | 
			
		||||
            Svg.delete_icon_ui()
 | 
			
		||||
                .SetClass("oh-delete-range")
 | 
			
		||||
                .SetClass("rounded-full w-6 h-6 block bg-black")
 | 
			
		||||
                .onClick(() => {
 | 
			
		||||
                    this._onDelete()
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let content = [deleteRange]
 | 
			
		||||
        let content: BaseUIElement;
 | 
			
		||||
        if (height > 2) {
 | 
			
		||||
            content = [startTime, deleteRange, endTime];
 | 
			
		||||
            content = new Combine([startTime, deleteRange, endTime]).SetClass("flex flex-col h-full").SetStyle("justify-content: space-between;");
 | 
			
		||||
        } else {
 | 
			
		||||
            content = new Combine([deleteRange]).SetClass("flex flex-col h-full").SetStyle("flex-content: center; overflow-x: unset;")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const el = new Combine(content)
 | 
			
		||||
            .SetClass("oh-timerange-inner").ConstructElement();
 | 
			
		||||
        const el = new Combine([content]).ConstructElement();
 | 
			
		||||
 | 
			
		||||
        el.style.top = (100 * OH.startTime(oh) / 24) + "%"
 | 
			
		||||
        el.style.height = (100 * (OH.endTime(oh) - OH.startTime(oh)) / 24) + "%"
 | 
			
		||||
        el.style.top = `${100 * OH.startTime(oh) / 24}%`
 | 
			
		||||
        el.style.height = `${100 * this.getHeight() / 24}%`
 | 
			
		||||
        return el;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,163 +5,16 @@ import {TextField} from "../Input/TextField";
 | 
			
		|||
import {DropDown} from "../Input/DropDown";
 | 
			
		||||
import {InputElement} from "../Input/InputElement";
 | 
			
		||||
import Translations from "../i18n/Translations";
 | 
			
		||||
import BaseUIElement from "../BaseUIElement";
 | 
			
		||||
import {VariableUiElement} from "../Base/VariableUIElement";
 | 
			
		||||
import Toggle from "../Input/Toggle";
 | 
			
		||||
 | 
			
		||||
export default class PublicHolidayInput extends InputElement<string> {
 | 
			
		||||
    IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
 | 
			
		||||
 | 
			
		||||
    private readonly _value: UIEventSource<string>;
 | 
			
		||||
    private readonly _dropdown: BaseUIElement;
 | 
			
		||||
    private readonly _mode: UIEventSource<string>;
 | 
			
		||||
    private readonly _startHour: BaseUIElement;
 | 
			
		||||
    private readonly _endHour: BaseUIElement;
 | 
			
		||||
    private _element: VariableUiElement;
 | 
			
		||||
 | 
			
		||||
    constructor(value: UIEventSource<string> = new UIEventSource<string>("")) {
 | 
			
		||||
        super();
 | 
			
		||||
        this._value = value;
 | 
			
		||||
 | 
			
		||||
        const dropdown = new DropDown(
 | 
			
		||||
            Translations.t.general.opening_hours.open_during_ph,
 | 
			
		||||
            [
 | 
			
		||||
                {shown: Translations.t.general.opening_hours.ph_not_known, value: ""},
 | 
			
		||||
                {shown: Translations.t.general.opening_hours.ph_closed, value: "off"},
 | 
			
		||||
                {shown:Translations.t.general.opening_hours.ph_open, value: " "}
 | 
			
		||||
            ]
 | 
			
		||||
        );
 | 
			
		||||
        this._dropdown = dropdown.SetStyle("display:inline-block;");
 | 
			
		||||
        this._mode = 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();
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        this._element = new VariableUiElement(this._mode.map(
 | 
			
		||||
            mode => {
 | 
			
		||||
                if (mode === " ") {
 | 
			
		||||
                    return new Combine([this._dropdown,
 | 
			
		||||
                        " ",
 | 
			
		||||
                        Translations.t.general.opening_hours.opensAt,
 | 
			
		||||
                        " ",
 | 
			
		||||
                        this._startHour,
 | 
			
		||||
                        " ",
 | 
			
		||||
                        Translations.t.general.opening_hours.openTill,
 | 
			
		||||
                        " ",
 | 
			
		||||
                        this._endHour]);
 | 
			
		||||
                }
 | 
			
		||||
                return this._dropdown;
 | 
			
		||||
 | 
			
		||||
            }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected InnerConstructElement(): HTMLElement {
 | 
			
		||||
        return this._element.ConstructElement();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 === "PH open"){
 | 
			
		||||
            return {
 | 
			
		||||
                mode: " "
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||
| 
						 | 
				
			
			@ -173,4 +26,96 @@ export default class PublicHolidayInput extends InputElement<string> {
 | 
			
		|||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private SetupDataSync(mode: UIEventSource<string>, startTime: UIEventSource<string>, endTime: UIEventSource<string>) {
 | 
			
		||||
 | 
			
		||||
        const value = this._value;
 | 
			
		||||
        value.addCallbackAndRun(ph => {
 | 
			
		||||
            if (ph === undefined) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            const parsed = OH.ParsePHRule(ph);
 | 
			
		||||
            if (parsed === null) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            mode.setData(parsed.mode)
 | 
			
		||||
            startTime.setData(parsed.start)
 | 
			
		||||
            endTime.setData(parsed.end)
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        // We use this as a 'addCallbackAndRun'
 | 
			
		||||
        mode.map(mode => {
 | 
			
		||||
                if (mode === undefined || mode === "") {
 | 
			
		||||
                    // not known
 | 
			
		||||
                    value.setData(undefined)
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
                if (mode === "off") {
 | 
			
		||||
                    value.setData("PH off");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (mode === "open") {
 | 
			
		||||
                    value.setData("PH open");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                // Open during PH with special hours
 | 
			
		||||
                if (startTime.data === undefined || endTime.data === undefined) {
 | 
			
		||||
                    // hours not filled in - not saveable
 | 
			
		||||
                    value.setData(undefined)
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
                const oh = `PH ${startTime.data}-${endTime.data}`
 | 
			
		||||
                value.setData(oh)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            }, [startTime, endTime]
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    protected InnerConstructElement(): HTMLElement {
 | 
			
		||||
        const dropdown = new DropDown(
 | 
			
		||||
            Translations.t.general.opening_hours.open_during_ph.Clone(),
 | 
			
		||||
            [
 | 
			
		||||
                {shown: Translations.t.general.opening_hours.ph_not_known.Clone(), value: ""},
 | 
			
		||||
                {shown: Translations.t.general.opening_hours.ph_closed.Clone(), value: "off"},
 | 
			
		||||
                {shown:Translations.t.general.opening_hours.ph_open_as_usual.Clone(), value: "open"},
 | 
			
		||||
                {shown: Translations.t.general.opening_hours.ph_open.Clone(), value: " "},
 | 
			
		||||
            ]
 | 
			
		||||
        ).SetClass("inline-block");
 | 
			
		||||
        /*
 | 
			
		||||
        * Either "" (unknown), " " (opened) or "off" (closed)
 | 
			
		||||
        * */
 | 
			
		||||
        const mode = dropdown.GetValue();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        const start = new TextField({
 | 
			
		||||
            placeholder: "starthour",
 | 
			
		||||
            htmlType: "time"
 | 
			
		||||
        }).SetClass("inline-block");
 | 
			
		||||
        const end = new TextField({
 | 
			
		||||
            placeholder: "starthour",
 | 
			
		||||
            htmlType: "time"
 | 
			
		||||
        }).SetClass("inline-block");
 | 
			
		||||
        
 | 
			
		||||
        const askHours = new Toggle(
 | 
			
		||||
            new Combine([
 | 
			
		||||
                Translations.t.general.opening_hours.opensAt.Clone(),
 | 
			
		||||
                start,
 | 
			
		||||
                Translations.t.general.opening_hours.openTill.Clone(),
 | 
			
		||||
                end
 | 
			
		||||
            ]),
 | 
			
		||||
            undefined,
 | 
			
		||||
            mode.map(mode => mode === " ")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        this.SetupDataSync(mode, start.GetValue(), end.GetValue())
 | 
			
		||||
        
 | 
			
		||||
        return new Combine([
 | 
			
		||||
            dropdown,
 | 
			
		||||
            askHours
 | 
			
		||||
        ]).ConstructElement()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +64,7 @@
 | 
			
		|||
    font-size: large;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    padding-right: 0.2em;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.oh-timecell-0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -119,35 +120,6 @@
 | 
			
		|||
    overflow: unset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.oh-timerange-inner {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    align-content: center;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    overflow-x: unset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.oh-timerange-inner-small {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    justify-content: space-between;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    width:100%;
 | 
			
		||||
}
 | 
			
		||||
.oh-delete-range{
 | 
			
		||||
    width: 1.5em;
 | 
			
		||||
    height: 1.5em;
 | 
			
		||||
    background:black;
 | 
			
		||||
    border-radius:0.75em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.oh-delete-range img {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    max-width: 2em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**** Opening hours visualization table ****/
 | 
			
		||||
 | 
			
		||||
.ohviz-table {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -151,7 +151,8 @@
 | 
			
		|||
            "open_24_7": "Opened around the clock",
 | 
			
		||||
            "ph_not_known": " ",
 | 
			
		||||
            "ph_closed": "closed",
 | 
			
		||||
            "ph_open": "opened"
 | 
			
		||||
            "ph_open": "opened with different hours",
 | 
			
		||||
            "ph_open_as_usual": "opened as usual"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "favourite": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								test.ts
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -8,6 +8,7 @@ import OpeningHoursPickerTable from "./UI/OpeningHours/OpeningHoursPickerTable";
 | 
			
		|||
import OpeningHoursPicker from "./UI/OpeningHours/OpeningHoursPicker";
 | 
			
		||||
import {OH, OpeningHour} from "./UI/OpeningHours/OpeningHours";
 | 
			
		||||
import {VariableUiElement} from "./UI/Base/VariableUIElement";
 | 
			
		||||
import PublicHolidayInput from "./UI/OpeningHours/PublicHolidayInput";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const tagsSource = new UIEventSource({
 | 
			
		||||
| 
						 | 
				
			
			@ -23,15 +24,8 @@ const tagsSource = new UIEventSource({
 | 
			
		|||
const state = new State(undefined)
 | 
			
		||||
State.state = state
 | 
			
		||||
 | 
			
		||||
const ohData = new UIEventSource<OpeningHour[]>([{
 | 
			
		||||
    weekday: 1,
 | 
			
		||||
    startHour: 10,
 | 
			
		||||
    startMinutes: 0
 | 
			
		||||
    , endHour: 12,
 | 
			
		||||
    endMinutes: 0
 | 
			
		||||
}])
 | 
			
		||||
new OpeningHoursPicker(ohData).AttachTo("maindiv")
 | 
			
		||||
new VariableUiElement(ohData.map(OH.ToString)).AttachTo("extradiv")
 | 
			
		||||
const ohData = new UIEventSource<string>("")
 | 
			
		||||
new OpeningHoursPicker().AttachTo("maindiv")
 | 
			
		||||
/*
 | 
			
		||||
const allSpecials = SpecialVisualizations.specialVisualizations.map(spec => {
 | 
			
		||||
    try{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -352,6 +352,47 @@ export default class    TagSpec extends  T{
 | 
			
		|||
                ]);
 | 
			
		||||
                equal(rules, "Tu 10:00-12:00; Su 13:00-17:00");
 | 
			
		||||
            }],
 | 
			
		||||
            ["JOIN OH with end hours", () =>{
 | 
			
		||||
                const rules = OH.ToString(
 | 
			
		||||
                    OH.MergeTimes([
 | 
			
		||||
 | 
			
		||||
                    {
 | 
			
		||||
                        weekday: 1,
 | 
			
		||||
                        endHour: 23,
 | 
			
		||||
                        endMinutes: 30,
 | 
			
		||||
                        startHour: 23,
 | 
			
		||||
                        startMinutes: 0
 | 
			
		||||
                    }, {
 | 
			
		||||
                        weekday: 1,
 | 
			
		||||
                        endHour: 24,
 | 
			
		||||
                        endMinutes: 0,
 | 
			
		||||
                        startHour: 23,
 | 
			
		||||
                        startMinutes: 30
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                ]));
 | 
			
		||||
                equal(rules, "Tu 23:00-00:00");
 | 
			
		||||
            }],            ["JOIN OH with overflowed hours", () =>{
 | 
			
		||||
                const rules = OH.ToString(
 | 
			
		||||
                    OH.MergeTimes([
 | 
			
		||||
 | 
			
		||||
                        {
 | 
			
		||||
                            weekday: 1,
 | 
			
		||||
                            endHour: 23,
 | 
			
		||||
                            endMinutes: 30,
 | 
			
		||||
                            startHour: 23,
 | 
			
		||||
                            startMinutes: 0
 | 
			
		||||
                        }, {
 | 
			
		||||
                            weekday: 1,
 | 
			
		||||
                            endHour: 0,
 | 
			
		||||
                            endMinutes: 0,
 | 
			
		||||
                            startHour: 23,
 | 
			
		||||
                            startMinutes: 30
 | 
			
		||||
                        },
 | 
			
		||||
 | 
			
		||||
                    ]));
 | 
			
		||||
                equal(rules, "Tu 23:00-00:00");
 | 
			
		||||
            }],
 | 
			
		||||
            ["OH 24/7", () => {
 | 
			
		||||
                const rules = OH.Parse("24/7");
 | 
			
		||||
                equal(rules.length, 7);
 | 
			
		||||
| 
						 | 
				
			
			@ -368,8 +409,10 @@ export default class    TagSpec extends  T{
 | 
			
		|||
                equal(rules, null);
 | 
			
		||||
            }],
 | 
			
		||||
            ["OH Parse PH 12:00-17:00", () => {
 | 
			
		||||
                const rules = PublicHolidayInput.LoadValue("PH 12:00-17:00");
 | 
			
		||||
                const rules = OH.ParsePHRule("PH 12:00-17:00");
 | 
			
		||||
                equal(rules.mode, " ");
 | 
			
		||||
                equal(rules.start, "12:00")
 | 
			
		||||
                equal(rules.end, "17:00")
 | 
			
		||||
            }],
 | 
			
		||||
            ["Round", () => {
 | 
			
		||||
                equal(Utils.Round(15), "15.0")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue