Butchering the UI framework

This commit is contained in:
Pieter Vander Vennet 2021-06-10 01:36:20 +02:00
parent 8d404b1ba9
commit 6415e195d1
90 changed files with 1012 additions and 3101 deletions

View file

@ -9,21 +9,28 @@ import Constants from "../../Models/Constants";
import opening_hours from "opening_hours";
export default class OpeningHoursVisualization extends UIElement {
private static readonly weekdays = [
Translations.t.general.weekdays.abbreviations.monday,
Translations.t.general.weekdays.abbreviations.tuesday,
Translations.t.general.weekdays.abbreviations.wednesday,
Translations.t.general.weekdays.abbreviations.thursday,
Translations.t.general.weekdays.abbreviations.friday,
Translations.t.general.weekdays.abbreviations.saturday,
Translations.t.general.weekdays.abbreviations.sunday,
]
private readonly _key: string;
constructor(tags: UIEventSource<any>, key: string) {
super(tags);
this._key = key;
this.ListenTo(UIEventSource.Chronic(60*1000)); // Automatically reload every minute
this.ListenTo(UIEventSource.Chronic(60 * 1000)); // Automatically reload every minute
this.ListenTo(UIEventSource.Chronic(500, () => {
return tags.data._country === undefined;
}));
}
}
private static GetRanges(oh: any, from: Date, to: Date): ({
isOpen: boolean,
isSpecial: boolean,
@ -38,7 +45,7 @@ export default class OpeningHoursVisualization extends UIElement {
const start = new Date(from);
// We go one day more into the past, in order to force rendering of holidays in the start of the period
start.setDate(from.getDate() - 1);
const iterator = oh.getIterator(start);
let prevValue = undefined;
@ -63,8 +70,8 @@ export default class OpeningHoursVisualization extends UIElement {
// simply closed, nothing special here
continue;
}
if(value.startDate < from){
if (value.startDate < from) {
continue;
}
// Get day: sunday is 0, monday is 1. We move everything so that monday == 0
@ -80,8 +87,190 @@ export default class OpeningHoursVisualization extends UIElement {
return new Date(d.setDate(diff));
}
InnerRender(): string | UIElement {
private allChangeMoments(ranges: {
const today = new Date();
today.setHours(0, 0, 0, 0);
const lastMonday = OpeningHoursVisualization.getMonday(today);
const nextSunday = new Date(lastMonday);
nextSunday.setDate(nextSunday.getDate() + 7);
const tags = this._source.data;
if (tags._country === undefined) {
return "Loading country information...";
}
let oh = null;
try {
// noinspection JSPotentiallyInvalidConstructorUsage
oh = new opening_hours(tags[this._key], {
lat: tags._lat,
lon: tags._lon,
address: {
country_code: tags._country
}
}, {tag_key: this._key});
} catch (e) {
console.log(e);
return new Combine([Translations.t.general.opening_hours.error_loading,
State.state?.osmConnection?.userDetails?.data?.csCount >= Constants.userJourney.tagsVisibleAndWikiLinked ?
`<span class='subtle'>${e}</span>`
: ""
]);
}
if (!oh.getState() && !oh.getUnknown()) {
// POI is currently closed
const nextChange: Date = oh.getNextChange();
if (
// Shop isn't gonna open anymore in this timerange
nextSunday < nextChange
// And we are already in the weekend to show next week
&& (today.getDay() == 0 || today.getDay() == 6)
) {
// We mover further along
lastMonday.setDate(lastMonday.getDate() + 7);
nextSunday.setDate(nextSunday.getDate() + 7);
}
}
// ranges[0] are all ranges for monday
const ranges = OpeningHoursVisualization.GetRanges(oh, lastMonday, nextSunday);
if (ranges.map(r => r.length).reduce((a, b) => a + b, 0) == 0) {
// Closed!
const opensAtDate = oh.getNextChange();
if (opensAtDate === undefined) {
const comm = oh.getComment() ?? oh.getUnknown();
if (!!comm) {
return new FixedUiElement(comm).SetClass("ohviz-closed");
}
if (oh.getState()) {
return Translations.t.general.opening_hours.open_24_7.SetClass("ohviz-closed")
}
return Translations.t.general.opening_hours.closed_permanently.SetClass("ohviz-closed")
}
const moment = `${opensAtDate.getDate()}/${opensAtDate.getMonth() + 1} ${OH.hhmm(opensAtDate.getHours(), opensAtDate.getMinutes())}`
return Translations.t.general.opening_hours.closed_until.Subs({date: moment}).SetClass("ohviz-closed")
}
const isWeekstable = oh.isWeekStable();
let [changeHours, changeHourText] = OpeningHoursVisualization.allChangeMoments(ranges);
// By default, we always show the range between 8 - 19h, in order to give a stable impression
// Ofc, a bigger range is used if needed
const earliestOpen = Math.min(8 * 60 * 60, ...changeHours);
let latestclose = Math.max(...changeHours);
// We always make sure there is 30m of leeway in order to give enough room for the closing entry
latestclose = Math.max(19 * 60 * 60, latestclose + 30 * 60)
const rows: UIElement[] = [];
const availableArea = latestclose - earliestOpen;
// @ts-ignore
const now = (100 * (((new Date() - today) / 1000) - earliestOpen)) / availableArea;
let header: UIElement[] = [];
if (now >= 0 && now <= 100) {
header.push(new FixedUiElement("").SetStyle(`left:${now}%;`).SetClass("ohviz-now"))
}
for (const changeMoment of changeHours) {
const offset = 100 * (changeMoment - earliestOpen) / availableArea;
if (offset < 0 || offset > 100) {
continue;
}
const el = new FixedUiElement("").SetStyle(`left:${offset}%;`).SetClass("ohviz-line");
header.push(el);
}
for (let i = 0; i < changeHours.length; i++) {
let changeMoment = changeHours[i];
const offset = 100 * (changeMoment - earliestOpen) / availableArea;
if (offset < 0 || offset > 100) {
continue;
}
const el = new FixedUiElement(
`<div style='margin-top: ${i % 2 == 0 ? '1.5em;' : "1%"}'>${changeHourText[i]}</div>`
)
.SetStyle(`left:${offset}%`)
.SetClass("ohviz-time-indication");
header.push(el);
}
rows.push(new Combine([`<td width="5%">&NonBreakingSpace;</td>`,
`<td style="position:relative;height:2.5em;">`,
new Combine(header), `</td>`]));
for (let i = 0; i < 7; i++) {
const dayRanges = ranges[i];
const isToday = (new Date().getDay() + 6) % 7 === i;
let weekday = OpeningHoursVisualization.weekdays[i];
let dateToShow = ""
if (!isWeekstable) {
const day = new Date(lastMonday)
day.setDate(day.getDate() + i);
dateToShow = "" + day.getDate() + "/" + (day.getMonth() + 1);
}
let innerContent: (string | UIElement)[] = [];
// Add the lines
for (const changeMoment of changeHours) {
const offset = 100 * (changeMoment - earliestOpen) / availableArea;
innerContent.push(new FixedUiElement("").SetStyle(`left:${offset}%;`).SetClass("ohviz-line"))
}
// Add the actual ranges
for (const range of dayRanges) {
if (!range.isOpen && !range.isSpecial) {
innerContent.push(
new FixedUiElement(range.comment ?? dateToShow).SetClass("ohviz-day-off"))
continue;
}
const startOfDay: Date = new Date(range.startDate);
startOfDay.setHours(0, 0, 0, 0);
// @ts-ignore
const startpoint = (range.startDate - startOfDay) / 1000 - earliestOpen;
// @ts-ignore
const width = (100 * (range.endDate - range.startDate) / 1000) / (latestclose - earliestOpen);
const startPercentage = (100 * startpoint / availableArea);
innerContent.push(
new FixedUiElement(range.comment ?? dateToShow).SetStyle(`left:${startPercentage}%; width:${width}%`).SetClass("ohviz-range"))
}
// Add line for 'now'
if (now >= 0 && now <= 100) {
innerContent.push(new FixedUiElement("").SetStyle(`left:${now}%;`).SetClass("ohviz-now"))
}
let clss = ""
if (isToday) {
clss = "ohviz-today"
}
rows.push(new Combine(
[`<td class="ohviz-weekday ${clss}">${weekday}</td>`,
`<td style="position:relative;" class="${clss}">`,
...innerContent,
`</td>`]))
}
return new Combine([
"<table class='ohviz' style='width:100%; word-break: normal; word-wrap: normal'>",
...rows.map(el => "<tr>" + el.Render() + "</tr>"),
"</table>"
]).SetClass("ohviz-container");
}
private static allChangeMoments(ranges: {
isOpen: boolean,
isSpecial: boolean,
comment: string,
@ -131,194 +320,4 @@ export default class OpeningHoursVisualization extends UIElement {
return [changeHours, changeHourText]
}
private static readonly weekdays = [
Translations.t.general.weekdays.abbreviations.monday,
Translations.t.general.weekdays.abbreviations.tuesday,
Translations.t.general.weekdays.abbreviations.wednesday,
Translations.t.general.weekdays.abbreviations.thursday,
Translations.t.general.weekdays.abbreviations.friday,
Translations.t.general.weekdays.abbreviations.saturday,
Translations.t.general.weekdays.abbreviations.sunday,
]
InnerRender(): string {
const today = new Date();
today.setHours(0, 0, 0, 0);
const lastMonday = OpeningHoursVisualization.getMonday(today);
const nextSunday = new Date(lastMonday);
nextSunday.setDate(nextSunday.getDate() + 7);
const tags = this._source.data;
if (tags._country === undefined) {
return "Loading country information...";
}
let oh = null;
try {
oh = new opening_hours(tags[this._key], {
lat: tags._lat,
lon: tags._lon,
address: {
country_code: tags._country
}
}, {tag_key: this._key});
} catch (e) {
console.log(e);
const msg = new Combine([Translations.t.general.opening_hours.error_loading,
State.state?.osmConnection?.userDetails?.data?.csCount >= Constants.userJourney.tagsVisibleAndWikiLinked ?
`<span class='subtle'>${e}</span>`
: ""
]);
return msg.Render();
}
if (!oh.getState() && !oh.getUnknown()) {
// POI is currently closed
const nextChange: Date = oh.getNextChange();
if (
// Shop isn't gonna open anymore in this timerange
nextSunday < nextChange
// And we are already in the weekend to show next week
&& (today.getDay() == 0 || today.getDay() == 6)
) {
// We mover further along
lastMonday.setDate(lastMonday.getDate() + 7);
nextSunday.setDate(nextSunday.getDate() + 7);
}
}
// ranges[0] are all ranges for monday
const ranges = OpeningHoursVisualization.GetRanges(oh, lastMonday, nextSunday);
if (ranges.map(r => r.length).reduce((a, b) => a + b, 0) == 0) {
// Closed!
const opensAtDate = oh.getNextChange();
if(opensAtDate === undefined){
const comm = oh.getComment() ?? oh.getUnknown();
if(!!comm){
return new FixedUiElement(comm).SetClass("ohviz-closed").Render();
}
if(oh.getState()){
return Translations.t.general.opening_hours.open_24_7.SetClass("ohviz-closed").Render()
}
return Translations.t.general.opening_hours.closed_permanently.SetClass("ohviz-closed").Render()
}
const moment = `${opensAtDate.getDate()}/${opensAtDate.getMonth() + 1} ${OH.hhmm(opensAtDate.getHours(), opensAtDate.getMinutes())}`
return Translations.t.general.opening_hours.closed_until.Subs({date: moment}).SetClass("ohviz-closed").Render()
}
const isWeekstable = oh.isWeekStable();
let [changeHours, changeHourText] = this.allChangeMoments(ranges);
// By default, we always show the range between 8 - 19h, in order to give a stable impression
// Ofc, a bigger range is used if needed
const earliestOpen = Math.min(8 * 60 * 60, ...changeHours);
let latestclose = Math.max(...changeHours);
// We always make sure there is 30m of leeway in order to give enough room for the closing entry
latestclose = Math.max(19 * 60 * 60, latestclose + 30 * 60)
const rows: UIElement[] = [];
const availableArea = latestclose - earliestOpen;
// @ts-ignore
const now = (100 * (((new Date() - today) / 1000) - earliestOpen)) / availableArea;
let header = "";
if (now >= 0 && now <= 100) {
header += new FixedUiElement("").SetStyle(`left:${now}%;`).SetClass("ohviz-now").Render()
}
for (const changeMoment of changeHours) {
const offset = 100 * (changeMoment - earliestOpen) / availableArea;
if (offset < 0 || offset > 100) {
continue;
}
const el = new FixedUiElement("").SetStyle(`left:${offset}%;`).SetClass("ohviz-line").Render();
header += el;
}
for (let i = 0; i < changeHours.length; i++) {
let changeMoment = changeHours[i];
const offset = 100 * (changeMoment - earliestOpen) / availableArea;
if (offset < 0 || offset > 100) {
continue;
}
const el = new FixedUiElement(
`<div style='margin-top: ${i % 2 == 0 ? '1.5em;' : "1%"}'>${changeHourText[i]}</div>`
)
.SetStyle(`left:${offset}%`)
.SetClass("ohviz-time-indication").Render();
header += el;
}
rows.push(new Combine([`<td width="5%">&NonBreakingSpace;</td>`,
`<td style="position:relative;height:2.5em;">${header}</td>`]));
for (let i = 0; i < 7; i++) {
const dayRanges = ranges[i];
const isToday = (new Date().getDay() + 6) % 7 === i;
let weekday = OpeningHoursVisualization.weekdays[i].Render();
let dateToShow = ""
if (!isWeekstable) {
const day = new Date(lastMonday)
day.setDate(day.getDate() + i);
dateToShow = "" + day.getDate() + "/" + (day.getMonth() + 1);
}
let innerContent: string[] = [];
// Add the lines
for (const changeMoment of changeHours) {
const offset = 100 * (changeMoment - earliestOpen) / availableArea;
innerContent.push(new FixedUiElement("").SetStyle(`left:${offset}%;`).SetClass("ohviz-line").Render())
}
// Add the actual ranges
for (const range of dayRanges) {
if (!range.isOpen && !range.isSpecial) {
innerContent.push(
new FixedUiElement(range.comment ?? dateToShow).SetClass("ohviz-day-off").Render())
continue;
}
const startOfDay: Date = new Date(range.startDate);
startOfDay.setHours(0, 0, 0, 0);
// @ts-ignore
const startpoint = (range.startDate - startOfDay) / 1000 - earliestOpen;
// @ts-ignore
const width = (100 * (range.endDate - range.startDate) / 1000) / (latestclose - earliestOpen);
const startPercentage = (100 * startpoint / availableArea);
innerContent.push(
new FixedUiElement(range.comment ?? dateToShow).SetStyle(`left:${startPercentage}%; width:${width}%`).SetClass("ohviz-range").Render())
}
// Add line for 'now'
if (now >= 0 && now <= 100) {
innerContent.push(new FixedUiElement("").SetStyle(`left:${now}%;`).SetClass("ohviz-now").Render())
}
let clss = ""
if (isToday) {
clss = "ohviz-today"
}
rows.push(new Combine(
[`<td class="ohviz-weekday ${clss}">${weekday}</td>`,
`<td style="position:relative;" class="${clss}">${innerContent.join("")}</td>`]))
}
return new Combine([
"<table class='ohviz' style='width:100%; word-break: normal; word-wrap: normal'>",
rows.map(el => "<tr>" + el.Render() + "</tr>").join(""),
"</table>"
]).SetClass("ohviz-container").Render();
}
}

View file

@ -5,7 +5,6 @@
*/
import OpeningHoursPicker from "./OpeningHoursPicker";
import {UIEventSource} from "../../Logic/UIEventSource";
import {UIElement} from "../UIElement";
import {VariableUiElement} from "../Base/VariableUIElement";
import Combine from "../Base/Combine";
import {FixedUiElement} from "../Base/FixedUiElement";
@ -14,21 +13,20 @@ import {InputElement} from "../Input/InputElement";
import PublicHolidayInput from "./PublicHolidayInput";
import Translations from "../i18n/Translations";
import {Utils} from "../../Utils";
import BaseUIElement from "../BaseUIElement";
export default class OpeningHoursInput extends InputElement<string> {
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly _value: UIEventSource<string>;
private readonly _ohPicker: UIElement;
private readonly _leftoverWarning: UIElement;
private readonly _phSelector: UIElement;
private readonly _element: BaseUIElement;
constructor(value: UIEventSource<string> = new UIEventSource<string>("")) {
super();
const leftoverRules = value.map<string[]>(str => {
if (str === undefined) {
return []
@ -61,11 +59,11 @@ export default class OpeningHoursInput extends InputElement<string> {
}
return "";
})
this._phSelector = new PublicHolidayInput(ph);
const phSelector = new PublicHolidayInput(ph);
function update() {
const regular = OH.ToString(rulesFromOhPicker.data);
const rules : string[] = [
const rules: string[] = [
regular,
...leftoverRules.data,
ph.data
@ -76,39 +74,35 @@ export default class OpeningHoursInput extends InputElement<string> {
rulesFromOhPicker.addCallback(update);
ph.addCallback(update);
this._leftoverWarning = new VariableUiElement(leftoverRules.map((leftovers: string[]) => {
const 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();
new FixedUiElement(leftovers.map(r => `${r}<br/>`).join("")).SetClass("subtle")
]);
}))
this._ohPicker = new OpeningHoursPicker(rulesFromOhPicker);
const ohPicker = new OpeningHoursPicker(rulesFromOhPicker);
this._element = new Combine([
leftoverWarning,
ohPicker,
phSelector
])
}
protected InnerConstructElement(): HTMLElement {
return this._element.ConstructElement()
}
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;
}

View file

@ -5,6 +5,7 @@ import Combine from "../Base/Combine";
import OpeningHoursPickerTable from "./OpeningHoursPickerTable";
import {OH, OpeningHour} from "./OpeningHours";
import {InputElement} from "../Input/InputElement";
import BaseUIElement from "../BaseUIElement";
export default class OpeningHoursPicker extends InputElement<OpeningHour[]> {
private readonly _ohs: UIEventSource<OpeningHour[]>;
@ -12,7 +13,7 @@ export default class OpeningHoursPicker extends InputElement<OpeningHour[]> {
private readonly _backgroundTable: OpeningHoursPickerTable;
private readonly _weekdays: UIEventSource<UIElement[]> = new UIEventSource<UIElement[]>([]);
private readonly _weekdays: UIEventSource<BaseUIElement[]> = new UIEventSource<BaseUIElement[]>([]);
constructor(ohs: UIEventSource<OpeningHour[]> = new UIEventSource<OpeningHour[]>([])) {
super();
@ -49,8 +50,12 @@ export default class OpeningHoursPicker extends InputElement<OpeningHour[]> {
}
InnerRender(): string {
return this._backgroundTable.Render();
InnerRender(): BaseUIElement {
return this._backgroundTable;
}
protected InnerConstructElement(): HTMLElement {
return this._backgroundTable.ConstructElement();
}
GetValue(): UIEventSource<OpeningHour[]> {

View file

@ -8,12 +8,13 @@ import {Utils} from "../../Utils";
import {OpeningHour} from "./OpeningHours";
import {InputElement} from "../Input/InputElement";
import Translations from "../i18n/Translations";
import BaseUIElement from "../BaseUIElement";
export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]> {
public readonly IsSelected: UIEventSource<boolean>;
private readonly weekdays: UIEventSource<UIElement[]>;
private readonly weekdays: UIEventSource<BaseUIElement[]>;
public static readonly days: UIElement[] =
public static readonly days: BaseUIElement[] =
[
Translations.t.general.weekdays.abbreviations.monday,
Translations.t.general.weekdays.abbreviations.tuesday,
@ -28,8 +29,8 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
private readonly source: UIEventSource<OpeningHour[]>;
constructor(weekdays: UIEventSource<UIElement[]>, source?: UIEventSource<OpeningHour[]>) {
super(weekdays);
constructor(weekdays: UIEventSource<BaseUIElement[]>, source?: UIEventSource<OpeningHour[]>) {
super();
this.weekdays = weekdays;
this.source = source ?? new UIEventSource<OpeningHour[]>([]);
this.IsSelected = new UIEventSource<boolean>(false);

View file

@ -48,10 +48,10 @@ export default class OpeningHoursRange extends UIElement {
}
InnerRender(): string {
InnerRender(): UIElement {
const oh = this._oh.data;
if (oh === undefined) {
return "";
return undefined;
}
const height = this.getHeight();
@ -62,7 +62,6 @@ export default class OpeningHoursRange extends UIElement {
return new Combine(content)
.SetClass("oh-timerange-inner")
.Render();
}
private getHeight(): number {

View file

@ -143,7 +143,7 @@ export default class PublicHolidayInput extends InputElement<string> {
}
}
InnerRender(): string {
InnerRender(): UIElement {
const mode = this._mode.data;
if (mode === " ") {
return new Combine([this._dropdown,
@ -154,9 +154,9 @@ export default class PublicHolidayInput extends InputElement<string> {
" ",
Translations.t.general.opening_hours.openTill,
" ",
this._endHour]).Render();
this._endHour]);
}
return this._dropdown.Render();
return this._dropdown;
}
GetValue(): UIEventSource<string> {