aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2020-09-28 03:10:54 +0200
committerHugo Hörnquist <hugo@lysator.liu.se>2020-09-28 03:10:54 +0200
commit04dcab7a429d9b034d41b5aca8bd715c4826de32 (patch)
tree6678fffe9c275eb2ae0562fa2834e3b793e5aed5
parentMade timeinput checkbox needlesly fancy. (diff)
downloadcalp-04dcab7a429d9b034d41b5aca8bd715c4826de32.tar.gz
calp-04dcab7a429d9b034d41b5aca8bd715c4826de32.tar.xz
Groundwork for adding new fields from frontend.
-rw-r--r--module/calp/html/vcomponent.scm17
-rw-r--r--module/calp/html/view/calendar.scm25
-rw-r--r--static/script.js239
-rw-r--r--static/style.scss12
4 files changed, 287 insertions, 6 deletions
diff --git a/module/calp/html/vcomponent.scm b/module/calp/html/vcomponent.scm
index 54bfb9e8..2497aa04 100644
--- a/module/calp/html/vcomponent.scm
+++ b/module/calp/html/vcomponent.scm
@@ -192,10 +192,19 @@
(type "text")
))))
- #;
- (input (@ (type "text")
- (list "known-fields")
- (placeholder "Nytt fält")))
+ (hr)
+
+ (div (@ (class "newfield"))
+ (input (@ (type "text")
+ (list "known-fields")
+ (placeholder "Nytt fält")))
+ (select (@ (name "TYPE"))
+ (option (@ (value "TEXT")) "Text"))
+ (span
+ (input (@ (type "text")
+ (placeholder "Värde")))))
+
+ (hr)
(input (@ (type "submit")))
diff --git a/module/calp/html/view/calendar.scm b/module/calp/html/view/calendar.scm
index a583d82b..64986b5c 100644
--- a/module/calp/html/view/calendar.scm
+++ b/module/calp/html/view/calendar.scm
@@ -296,4 +296,27 @@
;; TODO merge this into the event-set, add attribute
;; for non-displaying elements.
(div (@ (class "template") (id "popup-template"))
- ,(popup event (string-append "popup" (html-id event)))))))))
+ ,(popup event (string-append "popup" (html-id event))))))
+
+ ;; Auto-complets when adding new fields to a component
+ ;; Any string is however still valid.
+ (datalist (@ (id "known-fields"))
+ ,@(map (lambda (f)
+ `(option (@ (value ,f))))
+ '(CALSCALE
+ METHOD PRODID VERSION ATTACH
+ CATEGORIES CLASS COMMENT
+ DESCRIPTION GEO LOCATION
+ PERCENT-COMPLETE PRIORITY
+ RESOURCES STATUS SUMMARY
+ COMPLETED DTEND DUE DTSTART
+ DURATION FREEBUSY
+ TRANSP TZID TZNAME
+ TZOFFSETFROM TZOFFSETTO
+ TZURL ATTENDEE CONTACT
+ ORGANIZER RECURRENCE-ID
+ RELATED-TO URL EXDATE
+ RDATE RRULE ACTION REPEAT
+ TRIGGER CREATED DTSTAMP LAST-MODIFIED
+ SEQUENCE REQUEST-STATUS
+ ))))))
diff --git a/static/script.js b/static/script.js
index 069d9a2e..b94d0f2b 100644
--- a/static/script.js
+++ b/static/script.js
@@ -628,6 +628,222 @@ window.onload = function () {
el.oninput = update_inline_list;
}
+
+ for (let el of document.getElementsByClassName("newfield")) {
+ let [name, type_selector, value_field] = el.children;
+
+ /* TODO list fields */
+ /* TODO add and remove fields. See update_inline_list */
+
+ function update_value_field (el) {
+ let [name_field, type_selector, value_field] = el.children;
+
+
+ let value = makeElement('input');
+ let values = [value];
+
+
+ switch (name_field.value.toUpperCase()) {
+ case 'GEO':
+ value.type = 'number';
+ values.push(makeElement('input', {
+ type: 'number',
+ }));
+ break;
+
+ case 'CLASS':
+ // Add auto completion
+ break;
+
+ case 'ACTION':
+ // Add auto completion
+ break;
+
+ case 'TRANSP':
+ // Replace with toggle betwen OPAQUE and TRANSPARENT
+ break;
+
+ case 'PERCENT-COMPLETE':
+ value.min = 0;
+ value.max = 100;
+ break;
+
+ case 'PRIORITY':
+ value.min = 0;
+ value.max = 9;
+ break;
+
+ default:
+
+
+ switch (type_selector.options[type_selector.selectedIndex].value) {
+ case 'integer':
+ case 'float':
+ value.type = 'number';
+ break;
+
+ case 'uri':
+ value.type = 'url';
+ break;
+
+ case 'binary':
+ value.type = 'file';
+ break;
+
+ case 'date-time':
+ values.push(makeElement('input', {
+ type: 'time',
+ }));
+ /* fallthrough */
+ case 'date':
+ value.type = 'date';
+ break;
+
+ case 'cal-address':
+ value.type = 'email';
+ break;
+
+ case 'utc-offset':
+ value.type = 'time';
+ let lbl = makeElement('label');
+ let id = gensym();
+
+ lbl.setAttribute('for', id);
+
+ /* TODO make these labels stand out more */
+ lbl.appendChild(makeElement('span', {
+ className: 'plus',
+ innerText: '+',
+ }));
+ lbl.appendChild(makeElement('span', {
+ className: 'minus',
+ innerText: '-',
+ }));
+ values.splice(0,0,lbl);
+ values.splice(0,0, makeElement('input', {
+ type: 'checkbox',
+ style: 'display:none',
+ className: 'plusminuscheck',
+ id: id,
+ }));
+ break;
+
+ case 'period':
+ value.type = 'text';
+ // TODO validate /P\d*H/ typ
+ break;
+
+ case 'recur':
+ // TODO
+ default:
+ value.type = 'text';
+ }
+ }
+
+
+ value_field.innerHTML = '';
+ for (let v of values) {
+ console.log(v);
+ value_field.appendChild(v);
+ }
+ }
+
+ name.oninput = function () {
+ let types = valid_input_types[this.value.toUpperCase()];
+ type_selector.disabled = false;
+ if (types) {
+ type_selector.innerHTML = '';
+ for (let type of types) {
+ type_selector.appendChild(
+ makeElement('option', { value: type, innerText: type }))
+ }
+ if (types.length == 1) {
+ type_selector.disabled = true;
+ }
+ } else {
+ type_selector.innerHTML = '';
+ for (let type of all_types) {
+ type_selector.appendChild(
+ makeElement('option', { value: type, innerText: type }))
+ }
+ }
+
+ update_value_field(el);
+ }
+ type_selector.onchange = function () {
+ update_value_field(el);
+ }
+ }
+
+}
+
+let all_types = [
+ 'text',
+ 'uri',
+ 'binary',
+ 'float',
+ 'integer',
+ 'date-time',
+ 'date',
+ 'duration',
+ 'period',
+ 'utc-offset',
+ 'cal-address',
+ 'recur',
+]
+
+
+/* TODO
+ Todo map for which properties are valid on which object types
+*/
+
+
+let valid_input_types = {
+ 'CALSCALE': ['text'],
+ 'METHOD': ['text'],
+ 'PRODID': ['text'],
+ 'VERSION': ['text'],
+ 'ATTACH': ['uri', 'binary'],
+ 'CATEGORIES': [['text']],
+ 'CLASS': ['text'], // PUBLIC|PRIVATE|CONFIDENTIAL|*other*
+ 'COMMENT': ['text'],
+ 'DESCRIPTION': ['text'],
+ 'GEO': ['float'], // pair of floats
+ 'LOCATION': ['text'],
+ 'PERCENT-COMPLETE': ['integer'], // 0-100
+ 'PRIORITY': ['integer'], // 0-9
+ 'RESOURCES': [['text']],
+ 'STATUS': ['text'], // see 3.8.1.11
+ 'SUMMARY': ['text'],
+ 'COMPLETED': ['date-time'],
+ 'DTEND': ['date', 'date-time'],
+ 'DUE': ['date', 'date-time'],
+ 'DTSTART': ['date', 'date-time'],
+ 'DURATION': ['duration'],
+ 'FREEBUSY': [['period']],
+ 'TRANSP': ['text'], // OPAQUE|TRANSPARENT
+ 'TZID': ['text'],
+ 'TZNAME': ['text'],
+ 'TZOFFSETFROM': ['utc-offset'],
+ 'TZOFFSETTO': ['utc-offset'],
+ 'TZURL': ['uri'],
+ 'ATTENDEE': ['cal-address'],
+ 'CONTACT': ['text'],
+ 'ORGANIZER': ['cal-address'],
+ 'RECURRENCE-ID': ['date', 'date-time'],
+ 'RELATED-TO': ['text'],
+ 'URL': ['uri'],
+ 'EXDATE': [['date', 'date-time']],
+ 'RDATE': [['date', 'date-time', 'period']],
+ 'RRULE': ['recur'],
+ 'ACTION': ['text'], // AUDIO|DISPLAY|EMAIL|*other*
+ 'REPEAT': ['integer'],
+ 'TRIGGER': ['duration', 'date-time'],
+ 'CREATED': ['date-time'],
+ 'DTSTAMP': ['date-time'],
+ 'LAST-MODIFIED': ['date-time'],
+ 'SEQUENCE': ['integer'],
+ 'REQUEST-STATUS': ['text']
}
function close_popup(popup) {
@@ -754,7 +970,8 @@ class vcomponent {
function bind_properties (el, wide_event=false) {
el.properties = {}
let popup = document.getElementById("popup" + el.id);
- let children = el.getElementsByTagName("properties")[0].children;
+ // let children = el.getElementsByTagName("properties")[0].children;
+ let children = el.querySelector("vevent > properties").children;
for (let child of children) {
let field = child.tagName;
@@ -771,6 +988,26 @@ function bind_properties (el, wide_event=false) {
lst.push([s, f]);
}
+
+ /* edit tab */
+ for (let s of popup.querySelectorAll(`[name='${field}']`)) {
+ let f;
+ s.oninput = function () {
+ el.properties[s.name] = this.value;
+ }
+ switch (s.name) {
+ case 'description':
+ f = ((s, v) => s.innerHTML = v.format(s.dataset && s.dataset.fmt));
+ lst.push([s, f]);
+ break;
+ default:
+ f = ((s, v) => s.value = v);
+ lst.push([s, f]);
+ break;
+ }
+ }
+
+
/* Bind vcomponent fields for this event */
for (let s of el.querySelectorAll(field + " > :not(parameters)")) {
switch (s.tagName) {
diff --git a/static/style.scss b/static/style.scss
index c2848940..d69e8bcf 100644
--- a/static/style.scss
+++ b/static/style.scss
@@ -884,6 +884,18 @@ along with their colors.
}
+.plusminuschecked label {
+ color: black;
+}
+
+.plusminuscheck:checked ~ label .plus {
+ color: green;
+}
+
+.plusminuscheck:not(:checked) ~ label .minus {
+ color: red;
+}
+
.inline-edit {
input {
/* important since regular spec is much stronger...*/