Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
402 lines
13 KiB
402 lines
13 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import PopupElement, { PopupOptions } from "."; |
|
import { attachClickEvent } from "../../helpers/dom/clickEvent"; |
|
import mediaSizes from "../../helpers/mediaSizes"; |
|
import I18n, { i18n, LangPackKey } from "../../lib/langPack"; |
|
import InputField from "../inputField"; |
|
|
|
export default class PopupDatePicker extends PopupElement { |
|
protected controlsDiv: HTMLElement; |
|
protected monthTitle: HTMLElement; |
|
protected prevBtn: HTMLElement; |
|
protected nextBtn: HTMLElement; |
|
|
|
protected monthsContainer: HTMLElement; |
|
protected month: HTMLElement; |
|
|
|
protected minMonth: Date; |
|
protected maxMonth: Date; |
|
protected minDate: Date; |
|
protected maxDate: Date; |
|
protected selectedDate: Date; |
|
protected selectedMonth: Date; |
|
protected selectedEl: HTMLElement; |
|
|
|
protected timeDiv: HTMLDivElement; |
|
protected hoursInputField: InputField; |
|
protected minutesInputField: InputField; |
|
|
|
constructor(initDate: Date, public onPick: (timestamp: number) => void, protected options: Partial<{ |
|
noButtons: true, |
|
noTitle: true, |
|
minDate: Date, |
|
maxDate: Date |
|
withTime: true, |
|
showOverflowMonths: true |
|
}> & PopupOptions = {}) { |
|
super('popup-date-picker', { |
|
body: true, |
|
overlayClosable: true, |
|
buttons: options.noButtons ? [] : [{ |
|
langKey: 'JumpToDate', |
|
callback: () => { |
|
if(this.onPick) { |
|
this.onPick(this.selectedDate.getTime() / 1000 | 0); |
|
} |
|
} |
|
}, { |
|
langKey: 'Cancel', |
|
isCancel: true |
|
}], |
|
title: true, |
|
...options |
|
}); |
|
|
|
this.minDate = options.minDate || new Date('2013-08-01T00:00:00'); |
|
|
|
if(initDate < this.minDate) { |
|
initDate.setFullYear(this.minDate.getFullYear(), this.minDate.getMonth(), this.minDate.getDate()); |
|
} |
|
|
|
// Controls |
|
this.controlsDiv = document.createElement('div'); |
|
this.controlsDiv.classList.add('date-picker-controls'); |
|
|
|
this.prevBtn = document.createElement('button'); |
|
this.prevBtn.classList.add('btn-icon', 'tgico-down', 'date-picker-prev'); |
|
attachClickEvent(this.prevBtn, this.onPrevClick, {listenerSetter: this.listenerSetter}); |
|
|
|
this.nextBtn = document.createElement('button'); |
|
this.nextBtn.classList.add('btn-icon', 'tgico-down', 'date-picker-next'); |
|
attachClickEvent(this.nextBtn, this.onNextClick, {listenerSetter: this.listenerSetter}); |
|
|
|
this.monthTitle = document.createElement('div'); |
|
this.monthTitle.classList.add('date-picker-month-title'); |
|
|
|
this.controlsDiv.append(this.prevBtn, this.monthTitle, this.nextBtn); |
|
|
|
// Month |
|
this.monthsContainer = document.createElement('div'); |
|
this.monthsContainer.classList.add('date-picker-months'); |
|
attachClickEvent(this.monthsContainer, this.onDateClick, {listenerSetter: this.listenerSetter}); |
|
|
|
this.body.append(this.controlsDiv, this.monthsContainer); |
|
|
|
// Time inputs |
|
if(options.withTime) { |
|
this.timeDiv = document.createElement('div'); |
|
this.timeDiv.classList.add('date-picker-time'); |
|
|
|
const delimiter = document.createElement('div'); |
|
delimiter.classList.add('date-picker-time-delimiter'); |
|
delimiter.append(':'); |
|
|
|
const handleTimeInput = (max: number, inputField: InputField, onInput: (length: number) => void, onOverflow?: (number: number) => void) => { |
|
const maxString = '' + max; |
|
this.listenerSetter.add(inputField.input)('input', (e) => { |
|
let value = inputField.value.replace(/\D/g, ''); |
|
if(value.length > 2) { |
|
value = value.slice(0, 2); |
|
} else { |
|
if((value.length === 1 && +value[0] > +maxString[0]) || (value.length === 2 && +value > max)) { |
|
if(value.length === 2 && onOverflow) { |
|
onOverflow(+value[1]); |
|
} |
|
|
|
value = '0' + value[0]; |
|
} |
|
} |
|
|
|
inputField.setValueSilently(value); |
|
onInput(value.length); |
|
}); |
|
}; |
|
|
|
this.hoursInputField = new InputField({plainText: true}); |
|
this.minutesInputField = new InputField({plainText: true}); |
|
|
|
handleTimeInput(23, this.hoursInputField, (length) => { |
|
if(length === 2) { |
|
this.minutesInputField.input.focus(); |
|
} |
|
|
|
this.setTimeTitle(); |
|
}, (number) => { |
|
this.minutesInputField.value = (number + this.minutesInputField.value).slice(0, 2); |
|
}); |
|
handleTimeInput(59, this.minutesInputField, (length) => { |
|
if(!length) { |
|
this.hoursInputField.input.focus(); |
|
} |
|
|
|
this.setTimeTitle(); |
|
}); |
|
|
|
this.selectedDate = initDate; |
|
|
|
initDate.setMinutes(initDate.getMinutes() + 10); |
|
|
|
this.hoursInputField.setValueSilently(('0' + initDate.getHours()).slice(-2)); |
|
this.minutesInputField.setValueSilently(('0' + initDate.getMinutes()).slice(-2)); |
|
|
|
initDate.setHours(0, 0, 0, 0); |
|
|
|
this.timeDiv.append(this.hoursInputField.container, delimiter, this.minutesInputField.container); |
|
|
|
attachClickEvent(this.btnConfirm, () => { |
|
if(this.onPick) { |
|
this.selectedDate.setHours(+this.hoursInputField.value || 0, +this.minutesInputField.value || 0, 0, 0); |
|
this.onPick(this.selectedDate.getTime() / 1000 | 0); |
|
} |
|
|
|
this.hide(); |
|
}, {listenerSetter: this.listenerSetter}); |
|
|
|
this.body.append(this.timeDiv); |
|
|
|
this.prevBtn.classList.add('primary'); |
|
this.nextBtn.classList.add('primary'); |
|
} |
|
|
|
const popupCenterer = document.createElement('div'); |
|
popupCenterer.classList.add('popup-centerer'); |
|
popupCenterer.append(this.container); |
|
this.element.append(popupCenterer); |
|
|
|
//const passed = (initDate.getTime() - (initDate.getTimezoneOffset() * 60000)) % 86400000; |
|
//this.selectedDate = this.maxDate = new Date(initDate.getTime() - passed); |
|
initDate.setHours(0, 0, 0, 0); |
|
this.selectedDate = initDate; |
|
|
|
this.maxDate = options.maxDate || new Date(); |
|
this.maxDate.setHours(0, 0, 0, 0); |
|
|
|
this.selectedMonth = new Date(this.selectedDate); |
|
this.selectedMonth.setDate(1); |
|
|
|
this.maxMonth = new Date(this.maxDate); |
|
this.maxMonth.setDate(1); |
|
|
|
this.minMonth = new Date(this.minDate); |
|
this.minMonth.setHours(0, 0, 0, 0); |
|
this.minMonth.setDate(1); |
|
|
|
if(this.selectedMonth.getTime() === this.minMonth.getTime()) { |
|
this.prevBtn.setAttribute('disabled', 'true'); |
|
} |
|
|
|
if(this.selectedMonth.getTime() === this.maxMonth.getTime()) { |
|
this.nextBtn.setAttribute('disabled', 'true'); |
|
} |
|
|
|
if(options.noTitle) { |
|
this.setTitle = () => {}; |
|
} |
|
|
|
this.setTimeTitle(); |
|
this.setTitle(); |
|
this.setMonth(); |
|
} |
|
|
|
onPrevClick = (e: MouseEvent) => { |
|
this.selectedMonth.setMonth(this.selectedMonth.getMonth() - 1); |
|
this.setMonth(); |
|
|
|
if(this.selectedMonth.getTime() === this.minMonth.getTime()) { |
|
this.prevBtn.setAttribute('disabled', 'true'); |
|
} |
|
|
|
this.nextBtn.removeAttribute('disabled'); |
|
}; |
|
|
|
onNextClick = (e: MouseEvent) => { |
|
this.selectedMonth.setMonth(this.selectedMonth.getMonth() + 1); |
|
this.setMonth(); |
|
|
|
if(this.selectedMonth.getTime() === this.maxMonth.getTime()) { |
|
this.nextBtn.setAttribute('disabled', 'true'); |
|
} |
|
|
|
this.prevBtn.removeAttribute('disabled'); |
|
}; |
|
|
|
onDateClick = (e: MouseEvent) => { |
|
//cancelEvent(e); |
|
const target = e.target as HTMLElement; |
|
|
|
if(!target.dataset.timestamp) return; |
|
|
|
if(this.selectedEl) { |
|
if(this.selectedEl === target) return; |
|
this.selectedEl.classList.remove('active'); |
|
} |
|
|
|
this.selectedEl = target; |
|
|
|
target.classList.add('active'); |
|
const timestamp = +target.dataset.timestamp; |
|
|
|
this.selectedDate = new Date(timestamp); |
|
|
|
this.setTitle(); |
|
this.setTimeTitle(); |
|
}; |
|
|
|
public setTimeTitle() { |
|
if(this.btnConfirm && this.selectedDate) { |
|
let key: LangPackKey, args: any[] = []; |
|
const date = new Date(); |
|
date.setHours(0, 0, 0, 0); |
|
|
|
const timeOptions: Intl.DateTimeFormatOptions = { |
|
minute: '2-digit', |
|
hour: '2-digit' |
|
}; |
|
|
|
const sendDate = new Date(this.selectedDate.getTime()); |
|
sendDate.setHours(+this.hoursInputField.value, +this.minutesInputField.value); |
|
|
|
if(this.selectedDate.getTime() === date.getTime()) { |
|
key = 'Schedule.SendToday'; |
|
}/* else if(this.selectedDate.getTime() === (date.getTime() + 86400e3)) { |
|
dayStr = 'Tomorrow'; |
|
} */ else { |
|
key = 'Schedule.SendDate'; |
|
|
|
const dateOptions: Intl.DateTimeFormatOptions = { |
|
month: 'short', |
|
day: 'numeric' |
|
}; |
|
|
|
if(sendDate.getFullYear() !== date.getFullYear()) { |
|
dateOptions.year = 'numeric'; |
|
} |
|
|
|
args.push(new I18n.IntlDateElement({ |
|
date: sendDate, |
|
options: dateOptions |
|
}).element); |
|
} |
|
|
|
args.push(new I18n.IntlDateElement({ |
|
date: sendDate, |
|
options: timeOptions |
|
}).element); |
|
|
|
this.btnConfirm.firstChild.replaceWith(i18n(key, args)); |
|
} |
|
} |
|
|
|
public setTitle() { |
|
//const splitted = this.selectedDate.toString().split(' ', 3); |
|
//this.title.innerText = splitted[0] + ', ' + splitted[1] + ' ' + splitted[2]; |
|
this.title.textContent = ''; |
|
this.title.append(new I18n.IntlDateElement({ |
|
date: this.selectedDate, |
|
options: { |
|
day: 'numeric', |
|
month: 'long', |
|
weekday: 'short' |
|
} |
|
}).element); |
|
} |
|
|
|
private renderElement(disabled: boolean, innerText: string | HTMLElement = '') { |
|
const el = document.createElement('button'); |
|
el.classList.add('btn-icon', 'date-picker-month-date'); |
|
|
|
if(disabled) { |
|
el.setAttribute('disabled', 'true'); |
|
} |
|
|
|
if(innerText) { |
|
el.append(innerText); |
|
} |
|
|
|
return el; |
|
} |
|
|
|
public setMonth() { |
|
const firstDate = new Date(this.selectedMonth); |
|
|
|
const options: Intl.DateTimeFormatOptions = { |
|
year: 'numeric', |
|
month: this.timeDiv && mediaSizes.isMobile ? 'short' : 'long' |
|
}; |
|
|
|
this.monthTitle.textContent = ''; |
|
this.monthTitle.append(new I18n.IntlDateElement({date: firstDate, options}).element); |
|
//this.monthTitle.innerText = (this.timeDiv && mediaSizes.isMobile ? monthName.slice(0, 3) : monthName) + ' ' + this.selectedMonth.getFullYear(); |
|
|
|
if(this.month) { |
|
this.month.remove(); |
|
} |
|
|
|
this.month = document.createElement('div'); |
|
this.month.classList.add('date-picker-month'); |
|
|
|
const weekStartDate = new Date(); |
|
const day = weekStartDate.getDay(); |
|
if(day !== 1) { |
|
weekStartDate.setHours(-24 * (day - 1)); |
|
} |
|
|
|
for(let i = 0; i < 7; ++i) { |
|
const el = this.renderElement(true, new I18n.IntlDateElement({date: weekStartDate, options: {weekday: 'narrow'}}).element); |
|
el.classList.remove('date-picker-month-date'); |
|
el.classList.add('date-picker-month-day'); |
|
this.month.append(el); |
|
weekStartDate.setDate(weekStartDate.getDate() + 1); |
|
} |
|
|
|
// 0 - sunday |
|
let dayIndex = firstDate.getDay() - 1; |
|
if(dayIndex === -1) dayIndex = 7 - 1; |
|
|
|
const clonedDate = new Date(firstDate.getTime()); |
|
clonedDate.setDate(clonedDate.getDate() - dayIndex - 1); |
|
|
|
// Padding first week |
|
for(let i = 0; i < dayIndex; ++i) { |
|
if(this.options.showOverflowMonths) { |
|
clonedDate.setDate(clonedDate.getDate() + 1); |
|
this.month.append(this.renderElement(true, '' + clonedDate.getDate())); |
|
} else { |
|
this.month.append(this.renderElement(true)); |
|
} |
|
} |
|
|
|
do { |
|
const date = firstDate.getDate(); |
|
const el = this.renderElement(firstDate > this.maxDate || firstDate < this.minDate, '' + date); |
|
el.dataset.timestamp = '' + firstDate.getTime(); |
|
|
|
if(firstDate.getTime() === this.selectedDate.getTime()) { |
|
this.selectedEl = el; |
|
el.classList.add('active'); |
|
} |
|
|
|
this.month.append(el); |
|
|
|
firstDate.setDate(date + 1); |
|
} while(firstDate.getDate() !== 1); |
|
|
|
const remainder = this.month.childElementCount % 7; |
|
if(this.options.showOverflowMonths && remainder) { |
|
for(let i = remainder; i < 7; ++i) { |
|
this.month.append(this.renderElement(true, '' + firstDate.getDate())); |
|
firstDate.setDate(firstDate.getDate() + 1); |
|
} |
|
} |
|
|
|
const lines = Math.ceil(this.month.childElementCount / 7); |
|
this.container.dataset.lines = '' + lines; |
|
|
|
this.monthsContainer.append(this.month); |
|
} |
|
}
|
|
|