From 7949fcdc683d07689bad5da5d20bfa3eeb5a6a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Tue, 5 Sep 2023 01:25:00 +0200 Subject: Move frontend code to subdirectories, to simplify command line flags. --- static/ts/components/changelog.ts | 49 +++++++ static/ts/components/date-jump.ts | 40 ++++++ static/ts/components/date-time-input.ts | 119 +++++++++++++++++ static/ts/components/edit-rrule.ts | 75 +++++++++++ static/ts/components/input-list.ts | 120 +++++++++++++++++ static/ts/components/popup-element.ts | 201 +++++++++++++++++++++++++++++ static/ts/components/slider.ts | 101 +++++++++++++++ static/ts/components/tab-group-element.ts | 184 ++++++++++++++++++++++++++ static/ts/components/vevent-block.ts | 99 ++++++++++++++ static/ts/components/vevent-description.ts | 38 ++++++ static/ts/components/vevent-dl.ts | 35 +++++ static/ts/components/vevent-edit.ts | 179 +++++++++++++++++++++++++ static/ts/components/vevent.ts | 69 ++++++++++ 13 files changed, 1309 insertions(+) create mode 100644 static/ts/components/changelog.ts create mode 100644 static/ts/components/date-jump.ts create mode 100644 static/ts/components/date-time-input.ts create mode 100644 static/ts/components/edit-rrule.ts create mode 100644 static/ts/components/input-list.ts create mode 100644 static/ts/components/popup-element.ts create mode 100644 static/ts/components/slider.ts create mode 100644 static/ts/components/tab-group-element.ts create mode 100644 static/ts/components/vevent-block.ts create mode 100644 static/ts/components/vevent-description.ts create mode 100644 static/ts/components/vevent-dl.ts create mode 100644 static/ts/components/vevent-edit.ts create mode 100644 static/ts/components/vevent.ts (limited to 'static/ts/components') diff --git a/static/ts/components/changelog.ts b/static/ts/components/changelog.ts new file mode 100644 index 00000000..d08f7cb3 --- /dev/null +++ b/static/ts/components/changelog.ts @@ -0,0 +1,49 @@ +import { makeElement } from '../lib' +import { ComponentVEvent } from './vevent' +import { VEvent } from '../vevent' + +export { VEventChangelog } + +class VEventChangelog extends ComponentVEvent { + + readonly ul: HTMLElement + + constructor(uid?: string) { + super(uid); + + this.ul = makeElement('ul'); + } + + connectedCallback() { + this.replaceChildren(this.ul); + } + + redraw(data: VEvent) { + /* TODO only redraw what is needed */ + let children = [] + for (let [_, el] of data.changelog) { + let msg = ''; + switch (el.type) { + case 'property': + msg += `change ${el.name}: ` + msg += `from "${el.from}" to "${el.to}"` + break; + case 'calendar': + if (el.from === null && el.to === null) { + msg += '???' + } else if (el.from === null) { + msg += `set calendar to "${atob(el.to!)}"` + } else if (el.to === null) { + msg += `Remove calendar "${atob(el.from)}"` + } else { + msg += `Change calendar from "${atob(el.from)}" to "${atob(el.to)}"` + } + break; + } + + children.push(makeElement('li', { textContent: msg })); + } + + this.ul.replaceChildren(...children) + } +} diff --git a/static/ts/components/date-jump.ts b/static/ts/components/date-jump.ts new file mode 100644 index 00000000..fd3908ae --- /dev/null +++ b/static/ts/components/date-jump.ts @@ -0,0 +1,40 @@ +export { DateJump } + +/* Replace backend-driven [today] link with frontend, with one that + gets correctly set in the frontend. Similarly, update the go to + specific date button into a link which updates wheneven the date + form updates. +*/ +class DateJump extends HTMLElement { + + readonly golink: HTMLAnchorElement; + readonly input: HTMLInputElement; + + constructor() { + super(); + + this.golink = document.createElement('a') + this.golink.classList.add('btn'); + this.golink.textContent = "➔" + this.input = document.createElement('input') + this.input.type = 'date'; + } + + connectedCallback() { + + /* Form is just here so the css works out */ + let form = document.createElement('form'); + form.replaceChildren(this.input, this.golink); + this.replaceChildren(form); + + this.input.onchange = () => { + let date = this.input.valueAsDate!.format('~Y-~m-~d'); + this.golink.href = `${date}.html` + } + + let now = (new Date).format("~Y-~m-~d") + this.input.value = now; + /* onchange isn't triggered by manually setting the value */ + this.golink.href = `${now}.html` + } +} diff --git a/static/ts/components/date-time-input.ts b/static/ts/components/date-time-input.ts new file mode 100644 index 00000000..20e9a505 --- /dev/null +++ b/static/ts/components/date-time-input.ts @@ -0,0 +1,119 @@ +export { DateTimeInput } + +import { makeElement, parseDate } from '../lib' + + +/* '' */ +class DateTimeInput extends /* HTMLInputElement */ HTMLElement { + + readonly time: HTMLInputElement; + readonly date: HTMLInputElement; + + constructor() { + super(); + + this.date = makeElement('input', { + type: 'date' + }) as HTMLInputElement + + this.time = makeElement('input', { + type: 'time', + disabled: this.dateonly + }) as HTMLInputElement + } + + connectedCallback() { + /* This can be in the constructor for chromium, but NOT firefox... + Vivaldi 4.3.2439.63 stable + Mozilla Firefox 94.0.1 + */ + /* + https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes#boolean_attributes + https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute + */ + this.replaceChildren(this.date, this.time) + } + + static get observedAttributes() { + return ['dateonly'] + } + + attributeChangedCallback(name: string, _: string | null, to: string | null): void { + switch (name) { + case 'dateonly': + if (to == null) { + this.time.disabled = false + } else { + if (to == '' || to == name) { + this.time.disabled = true; + } else { + throw new TypeError(`Invalid value for attribute dateonly: ${to}`) + } + } + break; + } + } + + get dateonly(): boolean { + return this.hasAttribute('dateonly'); + } + + set dateonly(b: boolean) { + if (b) { + this.setAttribute('dateonly', ""); + } else { + this.removeAttribute('dateonly'); + } + } + + set value(date: Date) { + let [d, t] = date.format("~L~Y-~m-~dT~H:~M").split('T'); + this.date.value = d; + this.time.value = t; + + this.dateonly = date.dateonly; + } + + get value(): Date { + let dt; + let date = this.date.value; + if (this.dateonly) { + dt = parseDate(date); + dt.dateonly = true; + } else { + let time = this.time.value; + dt = parseDate(date + 'T' + time) + dt.dateonly = false; + } + return dt; + } + + get stringValue(): string { + if (this.dateonly) { + return this.value.format("~Y-~m-~d") + } else { + return this.value.format("~Y-~m-~dT~H:~M:~S") + } + } + + set stringValue(new_value: Date | string) { + let date, time, dateonly = false; + if (new_value instanceof Date) { + date = new_value.format("~L~Y-~m-~d"); + time = new_value.format("~L~H:~M:~S"); + dateonly = new_value.dateonly; + } else { + [date, time] = new_value.split('T') + } + this.dateonly = dateonly; + this.date.value = date; + this.time.value = time; + } + + addEventListener(type: string, proc: ((e: Event) => void)) { + if (type != 'input') throw "Only input supported"; + + this.date.addEventListener(type, proc); + this.time.addEventListener(type, proc); + } +} diff --git a/static/ts/components/edit-rrule.ts b/static/ts/components/edit-rrule.ts new file mode 100644 index 00000000..a361bdee --- /dev/null +++ b/static/ts/components/edit-rrule.ts @@ -0,0 +1,75 @@ +export { EditRRule } + +import { ComponentVEvent } from './vevent' +import { VEvent } from '../vevent' +import { vcal_objects } from '../globals' + +import { RecurrenceRule } from '../vevent' + +/* + Tab for editing the recurrence rule of a component +*/ +class EditRRule extends ComponentVEvent { + + constructor(uid?: string) { + super(uid); + + if (!this.template) { + throw 'vevent-edit-rrule template required'; + } + + let frag = this.template.content.cloneNode(true) as DocumentFragment + let body = frag.firstElementChild! + this.replaceChildren(body); + + for (let el of this.querySelectorAll('[name]')) { + el.addEventListener('input', () => { + // console.log(this); + let data = vcal_objects.get(this.uid)!; + let rrule = data.getProperty('rrule') + if (!rrule) { + console.warn('RRUle missing from object'); + return; + } + rrule = rrule as RecurrenceRule + + console.log(el.getAttribute('name'), (el as any).value); + rrule[el.getAttribute('name')!] = (el as any).value; + data.setProperty('rrule', rrule); + + }); + } + } + + connectedCallback() { + this.redraw(vcal_objects.get(this.uid)!) + } + + redraw(data: VEvent) { + + let rrule = data.getProperty('rrule') + if (!rrule) return; + rrule = rrule as RecurrenceRule + + for (let el of this.querySelectorAll('[name]')) { + + /* + el ought to be one of the tag types: + , ,