aboutsummaryrefslogtreecommitdiff
path: root/static/components/vevent-edit.ts
blob: 22e6409ac51aaef64b06ba8fefd20869aa5298e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
export { ComponentEdit }

import { ComponentVEvent } from './vevent'
import { InputList } from './input-list'
import { DateTimeInput } from './date-time-input'

import { vcal_objects } from '../globals'
import { VEvent, RecurrenceRule } from '../vevent'
import { create_event } from '../server_connect'
import { to_boolean } from '../lib'

/* <vevent-edit />
   Edit form for a given VEvent. Used as the edit tab of popups.
*/
class ComponentEdit extends ComponentVEvent {

    constructor(uid?: string) {
        super(uid);

        if (!this.template) {
            throw 'vevent-edit template required';
        }

        let frag = this.template.content.cloneNode(true) as DocumentFragment
        let body = frag.firstElementChild!
        this.replaceChildren(body);
    }

    connectedCallback() {

        /* Edit tab is rendered here. It's left blank server-side, since
           it only makes sense to have something here if we have javascript */

        let data = vcal_objects.get(this.uid)

        if (!data) {
            throw `Data missing for uid ${this.dataset.uid}.`
        }


        // return;

        /* Handle calendar dropdown */
        for (let el of this.getElementsByClassName('calendar-selection')) {
            for (let opt of el.getElementsByTagName('option')) {
                opt.selected = false;
            }
            if (data.calendar) {
                (el as HTMLSelectElement).value = data.calendar;
            }

            el.addEventListener('change', (e) => {
                let v = (e.target as HTMLSelectElement).selectedOptions[0].value
                let obj = vcal_objects.get(this.uid)!
                obj.calendar = v;
            });
        }

        this.redraw(data);

        // for (let el of this.getElementsByClassName("interactive")) {
        for (let el of this.querySelectorAll("[data-property]")) {
            // console.log(el);
            el.addEventListener('input', (e) => {
                let obj = vcal_objects.get(this.uid)
                // console.log(el, e);
                if (obj === undefined) {
                    throw 'No object with uid ' + this.uid
                }
                if (!(el instanceof HTMLInputElement
                    || el instanceof DateTimeInput
                    || el instanceof HTMLTextAreaElement
                    || el instanceof InputList
                )) {
                    console.log(el, 'not an HTMLInputElement');
                    return;
                }
                // console.log(`obj[${el.dataset.property!}] = `, el.value);
                obj.setProperty(
                    el.dataset.property!,
                    el.value)
            });
        }

        let wholeday_ = this.querySelector('[name="wholeday"]')
        if (wholeday_) {
            let wholeday = wholeday_ as HTMLInputElement

            if (data.getProperty('dtstart')?.dateonly) {
                wholeday.checked = true;
            }

            wholeday.addEventListener('click', () => {
                let chk = wholeday.checked
                let start = data!.getProperty('dtstart')
                let end = data!.getProperty('dtend')
                start.dateonly = chk
                end.dateonly = chk
                data!.setProperty('dtstart', start);
                data!.setProperty('dtend', end);
            });
        }

        let has_repeats_ = this.querySelector('[name="has_repeats"]')
        if (has_repeats_) {
            let has_repeats = has_repeats_ as HTMLInputElement;

            has_repeats.addEventListener('click', () => {
                /* TODO unselecting and reselecting this checkbox deletes all entered data.
                   Cache it somewhere */
                if (has_repeats.checked) {
                    vcal_objects.get(this.uid)!.setProperty('rrule', new RecurrenceRule())
                } else {
                    /* TODO is this a good way to remove a property ? */
                    vcal_objects.get(this.uid)!.setProperty('rrule', undefined)
                }
            })
        }

        let submit = this.querySelector('form') as HTMLFormElement
        /* TODO If start or end DATE is changed, only allow THIS */
        /* if only time component was changed, allow all */
        submit.addEventListener('submit', (e) => {
            console.log(submit, e);
            // submit button pressed (e.submitter);
            let submit_type = 'all';
            if (e.submitter) {
                submit_type = e.submitter.dataset.key!;
            }
            create_event(submit_type, vcal_objects.get(this.uid)!);

            e.preventDefault();
            return false;
        });
    }

    redraw(data: VEvent) {
        /* We only update our fields, instead of reinstansiating
           ourselves from the template, in hope that it's faster */


        for (let el of this.querySelectorAll("[data-property]")) {
            if (!(el instanceof HTMLElement)) continue;
            let p = el.dataset.property!;
            let d: any;
            if ((d = data.getProperty(p))) {
                /*
                  https://stackoverflow.com/questions/57157830/how-can-i-specify-the-sequence-of-running-nested-web-components-constructors
                */
                window.setTimeout(() => {
                    /* NOTE Some specific types might require special formatting
                    here. But due to my custom components implementing custom
                    `.value' procedures, we might not need any special cases
                    here */
                    /* Technically we just want to cast to HTMLElement with
                    value field here, but multiple types implement it
                    sepparately, and no common interface exist */
                    (el as HTMLInputElement).value = d;
                });
            }
        }

        let el = this.querySelector('[name="has_repeats"]')
        if (el) {
            (el as HTMLInputElement).checked = to_boolean(data.getProperty('rrule'))
        }

        if (data.calendar) {
            for (let el of this.getElementsByClassName('calendar-selection')) {
                (el as HTMLSelectElement).value = data.calendar;
            }
        }
    }
}