First iteration of the timepicker
This commit is contained in:
parent
d1f286f466
commit
2a704a2b1d
7 changed files with 110 additions and 13 deletions
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
State.ts
2
State.ts
|
@ -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 = {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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?",
|
||||||
|
|
|
@ -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{
|
||||||
|
|
Loading…
Reference in a new issue