First iteration of the timepicker

This commit is contained in:
Pieter Vander Vennet 2020-10-04 12:55:44 +02:00
parent d1f286f466
commit 2a704a2b1d
7 changed files with 110 additions and 13 deletions

View file

@ -1,3 +1,5 @@
import {Utils} from "../Utils";
export interface OpeningHour { export interface OpeningHour {
weekday: number, // 0 is monday, 1 is tuesday, ... weekday: number, // 0 is monday, 1 is tuesday, ...
startHour: number, startHour: number,
@ -7,6 +9,35 @@ export interface OpeningHour {
} }
export class OpeningHourUtils { export class OpeningHourUtils {
private static readonly days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
private static readonly daysIndexed = {
mo: 0,
tu: 1,
we: 2,
th: 3,
fr: 4,
sa: 5,
su: 6
}
public static ToString(ohs: OpeningHour[]) {
const parts = [];
function hhmm(h, m) {
return Utils.TwoDigits(h) + ":" + Utils.TwoDigits(m);
}
for (const oh of ohs) {
parts.push(
OpeningHourUtils.days[oh.weekday] + " " + hhmm(oh.startHour, oh.startMinutes) + "-" + hhmm(oh.endHour, oh.endMinutes)
)
}
return parts.join("; ")+";"
}
/** /**
* Merge duplicate opening-hour element in place. * Merge duplicate opening-hour element in place.
* Returns true if something changed * Returns true if something changed
@ -18,12 +49,12 @@ export class OpeningHourUtils {
const newList = []; const newList = [];
while (queue.length > 0) { while (queue.length > 0) {
let maybeAdd = queue.pop(); let maybeAdd = queue.pop();
let doAddEntry = true; let doAddEntry = true;
if(maybeAdd.weekday == undefined){ if(maybeAdd.weekday == undefined){
doAddEntry = false; doAddEntry = false;
} }
for (let i = newList.length - 1; i >= 0 && doAddEntry; i--) { for (let i = newList.length - 1; i >= 0 && doAddEntry; i--) {
let guard = newList[i]; let guard = newList[i];
if (maybeAdd.weekday != guard.weekday) { if (maybeAdd.weekday != guard.weekday) {
@ -60,7 +91,7 @@ export class OpeningHourUtils {
endHour = maybeAdd.endHour; endHour = maybeAdd.endHour;
endMinutes = maybeAdd.endMinutes; endMinutes = maybeAdd.endMinutes;
} }
queue.push({ queue.push({
startHour: startHour, startHour: startHour,
startMinutes: startMinutes, startMinutes: startMinutes,
@ -107,5 +138,47 @@ export class OpeningHourUtils {
return OpeningHourUtils.startTime(mightLieIn) <= OpeningHourUtils.endTime(checked) && return OpeningHourUtils.startTime(mightLieIn) <= OpeningHourUtils.endTime(checked) &&
OpeningHourUtils.endTime(checked) <= OpeningHourUtils.endTime(mightLieIn) OpeningHourUtils.endTime(checked) <= OpeningHourUtils.endTime(mightLieIn)
} }
static Parse(str: string) {
if (str === undefined || str === "") {
return []
}
const parts = str.toLowerCase().split(";");
const ohs = []
function parseTime(hhmm) {
const spl = hhmm.trim().split(":");
return [Number(spl[0].trim()), Number(spl[1].trim())]
}
for (const part of parts) {
if(part === ""){
continue;
}
try {
const partSplit = part.trim().split(" ");
const weekday = OpeningHourUtils.daysIndexed[partSplit[0]]
const timings = partSplit[1].split("-");
const start = parseTime(timings[0])
const end = parseTime(timings[1]);
const oh: OpeningHour = {
weekday: weekday,
startHour: start[0],
startMinutes: start[1],
endHour: end[0],
endMinutes: end[1],
}
ohs.push(oh);
} catch (e) {
console.error("Could not parse opening hours part", part, ", skipping it due to ", e)
}
}
return ohs;
}
} }

View file

@ -23,7 +23,7 @@ export default class State {
// The singleton of the global state // The singleton of the global state
public static state: State; public static state: State;
public static vNumber = "0.0.9b"; public static vNumber = "0.1.0";
// The user journey states thresholds when a new feature gets unlocked // The user journey states thresholds when a new feature gets unlocked
public static userJourney = { public static userJourney = {

View file

@ -14,7 +14,7 @@ export default class OpeningHoursPicker extends InputElement<OpeningHour[]> {
private readonly _weekdays: UIEventSource<UIElement[]> = new UIEventSource<UIElement[]>([]); private readonly _weekdays: UIEventSource<UIElement[]> = new UIEventSource<UIElement[]>([]);
constructor(ohs: UIEventSource<OpeningHour[]>) { constructor(ohs: UIEventSource<OpeningHour[]> = new UIEventSource<OpeningHour[]>([])) {
super(); super();
this._ohs = ohs; this._ohs = ohs;
this._backgroundTable = new OpeningHoursPickerTable(this._weekdays); this._backgroundTable = new OpeningHoursPickerTable(this._weekdays);

View file

@ -48,8 +48,8 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour> {
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(id => `<td id="${this.id}-timecell-${id}-${h}-30" class="oh-timecell oh-timecell-half"><div class="oh-timecell-inner"></div></td>`, 7) +
'</tr>'; '</tr>';
} }
let days = OpeningHoursPickerTable.days.join("</th><th>"); let days = OpeningHoursPickerTable.days.join("</th><th width='14%'>");
return `<table id="oh-table-${this.id}" class="oh-table"><tr><th></th><th>${days}</tr>${rows}</table>`; return `<table id="oh-table-${this.id}" class="oh-table"><tr><th></th><th width='14%'>${days}</th></tr>${rows}</table>`;
} }
protected InnerUpdate() { protected InnerUpdate() {

View file

@ -8,13 +8,15 @@ import {UIElement} from "../UIElement";
import {UIEventSource} from "../../Logic/UIEventSource"; import {UIEventSource} from "../../Logic/UIEventSource";
import CombinedInputElement from "./CombinedInputElement"; import CombinedInputElement from "./CombinedInputElement";
import SimpleDatePicker from "./SimpleDatePicker"; import SimpleDatePicker from "./SimpleDatePicker";
import OpeningHoursPicker from "./OpeningHours/OpeningHoursPicker";
import {OpeningHour, OpeningHourUtils} from "../../Logic/OpeningHours";
interface TextFieldDef { interface TextFieldDef {
name: string, name: string,
explanation: string, explanation: string,
isValid: ((s: string, country?: string) => boolean), isValid: ((s: string, country?: string) => boolean),
reformat?: ((s: string, country?: string) => string), reformat?: ((s: string, country?: string) => string),
inputHelper?: (value:UIEventSource<string>) => InputElement<string>, inputHelper?: (value: UIEventSource<string>) => InputElement<string>,
} }
export default class ValidatedTextField { export default class ValidatedTextField {
@ -141,6 +143,24 @@ export default class ValidatedTextField {
return parsePhoneNumberFromString(str, country?.toUpperCase())?.isValid() ?? false return parsePhoneNumberFromString(str, country?.toUpperCase())?.isValid() ?? false
}, },
(str, country: any) => parsePhoneNumberFromString(str, country?.toUpperCase()).formatInternational() (str, country: any) => parsePhoneNumberFromString(str, country?.toUpperCase()).formatInternational()
),
ValidatedTextField.tp(
"opening_hours",
"Has extra elements to easily input when a POI is opened",
(s, country) => true, // TODO
str => str, // TODO reformat with opening_hours.js
(value) => {
const input = new InputElementMap<OpeningHour[], string>(new OpeningHoursPicker(),
(a, b) => a === b,
ohs => OpeningHourUtils.ToString(ohs),
str => OpeningHourUtils.Parse(str)
)
input.GetValue().addCallback(latest => {
console.log(latest);
value.setData(latest);
})
return input;
}
) )
] ]

View file

@ -214,6 +214,14 @@
"type": "email" "type": "email"
} }
}, },
{
"render": "Shop is open {opening_hours}",
"question": "When is this shop opened?",
"freeform": {
"key": "opening_hours",
"type": "opening_hours"
}
},
{ {
"question": { "question": {
"en": "Does this shop sell bikes?", "en": "Does this shop sell bikes?",

View file

@ -92,10 +92,8 @@
} }
.oh-timerange-inner input { .oh-timerange-inner input {
width: calc(100% - 2em); width: 100%;
box-sizing: border-box; box-sizing: border-box;
margin-left: 1em;
margin-right:1em;
} }
.oh-timerange-inner-small { .oh-timerange-inner-small {
@ -109,8 +107,6 @@
.oh-timerange-inner-small input { .oh-timerange-inner-small input {
width: min-content; width: min-content;
box-sizing: border-box; box-sizing: border-box;
margin-left: 1em;
margin-right:1em;
} }
.oh-delete-range{ .oh-delete-range{