From 24e06a13894f885bbb75b79beaa43d1c6fdfbae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Mon, 17 May 2021 00:53:51 +0200 Subject: Rework date-time input to be cleaner + work again. --- doc/ref/javascript/binders.texi | 10 +++++++ doc/ref/javascript/date_time.texi | 2 ++ module/calp/html/vcomponent.scm | 63 +++++++++++++++++---------------------- static/binders.js | 34 +++++++++++++++++++++ static/date_time.js | 54 ++++++++++++++++++--------------- static/script.js | 26 ++++++++++++---- static/style.scss | 28 ----------------- static/vcal.js | 39 ------------------------ 8 files changed, 124 insertions(+), 132 deletions(-) diff --git a/doc/ref/javascript/binders.texi b/doc/ref/javascript/binders.texi index 2b64b230..0e38411b 100644 --- a/doc/ref/javascript/binders.texi +++ b/doc/ref/javascript/binders.texi @@ -1,5 +1,7 @@ @node binders +@cindex binder +@cindex binders @subsection binders.js The bind system allows HTML-elements to specify that they want to be @@ -45,3 +47,11 @@ Binder for the wholeday toggle button. While CSS would suffice, this sets the disabled flags on the time inputs, giving a better user experience. @end defun + +@defun bind_date_time el e +@anchor{bind_date_time} +For @code{date_time} dummy component. Propagates gets and sets to +underlying input fields. + +Note: @emph{Must} be called @emph{after} @code{init_date_time}. +@end defun diff --git a/doc/ref/javascript/date_time.texi b/doc/ref/javascript/date_time.texi index fb2563f1..b2c5db92 100644 --- a/doc/ref/javascript/date_time.texi +++ b/doc/ref/javascript/date_time.texi @@ -36,4 +36,6 @@ components. We nevertheless use it here since we are emulating an input element. @end defivar +See also @pxref{bind_date_time} + @end defun diff --git a/module/calp/html/vcomponent.scm b/module/calp/html/vcomponent.scm index 4852390c..7530de91 100644 --- a/module/calp/html/vcomponent.scm +++ b/module/calp/html/vcomponent.scm @@ -160,44 +160,35 @@ (end (prop ev 'DTEND))) `(div (@ (class "timeinput")) - (input (@ (type "date") - (name "dtstart-date") - (style "grid-column:1;grid-row:2") - (class "bind") - (data-property "--dtstart-date") - (value ,(date->string (as-date start))))) - - (input (@ (type "date") - (name "dtend-date") - (style "grid-column:1;grid-row:3") - (class "bind") - (data-property "--dtend-date") - ,@(when end `((value ,(date->string (as-date end))))))) + ,@(with-label + "Starttid" + `(div (@ (class "date-time bind") + (data-bindby "bind_date_time") + (name "dtstart")) + (input (@ (type "date") + (value ,(date->string (as-date start))))) + (input (@ (type "time") + (value ,(time->string (as-time start))))))) ,@(with-label - "Heldag?" - `(input (@ (type "checkbox") - (class "bind") - (data-bindby "bind_wholeday") - (style "display:none") - (name "wholeday")))) - - (input (@ (type "time") - (name "dtstart-time") - (class "bind") - (data-property "--dtstart-time") - (style "grid-column:3;grid-row:2;" - ,(when (date? start) "display:none")) - (value ,(time->string (as-time start))))) - - (input (@ (type "time") - (name "dtend-time") - (class "bind") - (data-property "--dtend-time") - (style "grid-column:3;grid-row:3;" - ,(when (date? end) "display:none")) - ,@(when end `((value ,(time->string (as-time end))))) - )))) + "Sluttid" + `(div (@ (class "date-time bind") + (data-bindby "bind_date_time") + (name "dtend")) + (input (@ (type "date") + ,@(when end `((value ,(date->string (as-date end))))))) + (input (@ (type "time") + ,@(when end `((value ,(time->string (as-time end))))))))) + + (div + ,@(with-label + "Heldag?" + `(input (@ (type "checkbox") + (class "bind") + (data-bindby "bind_wholeday") + (name "wholeday"))))) + + )) ,@(with-label "Plats" diff --git a/static/binders.js b/static/binders.js index f6c306bd..72550191 100644 --- a/static/binders.js +++ b/static/binders.js @@ -110,3 +110,37 @@ function bind_wholeday(el, e) { } }); } + + +/* used for dtstart and dtend input boxes + init_date_time MUST be called beforehand +*/ +function bind_date_time(el, e) { + e.addEventListener('input', function () { + let dt = el.properties[e.name].value; + if (e.value == '') return; + let y, m, d, h, s; + switch (this.type) { + case 'date': + [y,m,d] = this.value.split('-') + dt.setYear(Number(y)/* - 1900 */); + dt.setMonth(Number(m) - 1); + dt.setDate(d); + break; + case 'time': + [h,m,s] = this.value.split(':') + dt.setHours(Number(h)); + dt.setMinutes(Number(m)); + dt.setSeconds(0); + break; + default: + console.log("How did you get here??? ", e); + } + + el.properties[e.name] = dt; + }); + + el.properties.get_callback_list(e.name).push( + [e, (s, v) => s.value = v.format("~Y-~m-~dT~H:~M")]); + +} diff --git a/static/date_time.js b/static/date_time.js index 274d476f..8b7249dd 100644 --- a/static/date_time.js +++ b/static/date_time.js @@ -1,30 +1,36 @@ -function init_date_time() { - for (let dt of document.getElementsByClassName("date-time")) { - dt.time = dt.querySelector('[type=time]'); - dt.date = dt.querySelector('[type=date]'); +function init_date_time_single(dt) { + dt.time = dt.querySelector('[type=time]'); + dt.date = dt.querySelector('[type=date]'); - Object.defineProperty(dt, 'value', { - get: () => (dt.date.value && dt.time.value) - // TODO wrapping tag - ? dt.date.value + "T" + dt.time.value - : "", - set: (v) => [dt.date.value, dt.time.value] = v.split("T"), - }); + Object.defineProperty(dt, 'value', { + get: () => (dt.date.value && dt.time.value) + // TODO wrapping tag + ? dt.date.value + "T" + dt.time.value + : "", + set: (v) => [dt.date.value, dt.time.value] = v.split("T"), + }); - Object.defineProperty(dt, 'name', { - get: () => dt.attributes.name.value - }); + Object.defineProperty(dt, 'name', { + get: () => dt.attributes.name.value + }); - dt._addEventListener = dt.addEventListener; - dt.addEventListener = function (field, proc) { - switch (field) { - case 'input': - dt.time.addEventListener(field, proc); - dt.date.addEventListener(field, proc); - break; - default: - dt._addEventListener(field, proc); - } + dt._addEventListener = dt.addEventListener; + dt.addEventListener = function (field, proc) { + /* input events are propagated to children + other events target ourselves */ + switch (field) { + case 'input': + dt.time.addEventListener(field, proc); + dt.date.addEventListener(field, proc); + break; + default: + dt._addEventListener(field, proc); } } } + +function init_date_time() { + for (let dt of document.getElementsByClassName("date-time")) { + init_date_time_single(dt); + } +} diff --git a/static/script.js b/static/script.js index 6a561757..8042c341 100644 --- a/static/script.js +++ b/static/script.js @@ -14,18 +14,30 @@ class EventCreator { } create_empty_event () { + /* TODO this doesn't clone JS attributes */ + let event = document.getElementById("event-template") .firstChild.cloneNode(true); let popup = document.getElementById("popup-template") .firstChild.cloneNode(true); - /* TODO shouldn't this use transferListeners (or similar)? + /* -------------------- */ + /* Manually transfer or recreate attributes which we still need */ + /* TODO shouldn't these use transferListeners (or similar)? See input_list.js:transferListeners */ + + for (let dt of popup.getElementsByClassName("date-time")) { + init_date_time_single(dt); + } + popup.getElementsByClassName("edit-form")[0].onsubmit = function () { create_event(event); return false; /* stop default */ } + /* -------------------- */ + /* Fix tabs for newly created popup */ + let id = gensym ("__js_event"); // TODO remove button? @@ -43,8 +55,12 @@ class EventCreator { let nav = popup.getElementsByClassName("popup-control")[0]; bind_popup_control(nav); + /* -------------------- */ + // TODO download links + /* -------------------- */ + event.id = id; popup.id = "popup" + id; @@ -261,6 +277,8 @@ window.onload = function () { sch.update(d); }, 1000 * 60); + init_date_time(); + /* Is event creation active? */ if (EDIT_MODE) { let eventCreator = new EventCreator; @@ -294,7 +312,7 @@ window.onload = function () { let popupElement = document.getElementById("popup" + event.id); open_popup(popupElement); - popupElement.querySelector("input[name='summary']").focus(); + popupElement.querySelector("input[name='summary']").focus(); }); } @@ -311,7 +329,6 @@ window.onload = function () { */ el.parentElement.removeAttribute("href"); - /* TODO this doesn't yet apply to newly created events */ let popup = document.getElementById("popup" + el.id); popup.getElementsByClassName("edit-form")[0].onsubmit = function () { create_event(el); @@ -322,7 +339,7 @@ window.onload = function () { if (el.closest(".longevents")) { new VComponent(el, true); } else { - new VComponent(el, true); + new VComponent(el, false); } } @@ -379,7 +396,6 @@ window.onload = function () { // init_arbitary_kv(); - init_date_time(); init_input_list(); } diff --git a/static/style.scss b/static/style.scss index 202e3a34..052c9a93 100644 --- a/static/style.scss +++ b/static/style.scss @@ -881,34 +881,6 @@ along with their colors. } .timeinput { - - display: grid; - grid-template-columns: 1fr [lbl-start] 1ch 1fr 1ch [lbl-end]; - grid-template-rows: [lbl-start] 0.7fr 1fr 1fr 0.3fr [lbl-end]; - - label { - background-color: rgba(10,20,30,0.7); - color: white; - border-radius: 1em; - - grid-column: lbl-start / lbl-end; - grid-row: lbl-start / lbl-end; - - text-align: center; - - user-select: none; - - z-index: 1; - - } - - input { - z-index: 2; - } - - input:checked ~ input { - z-index: 0; - } } } diff --git a/static/vcal.js b/static/vcal.js index 366b879a..13c489b8 100644 --- a/static/vcal.js +++ b/static/vcal.js @@ -80,45 +80,6 @@ class VComponent { /* checkbox for whole day */ - for (let field of ['dtstart', 'dtend']) { - - this.get_callback_list(`--${field}-time`).push( - [el, (el, v) => { let date = this[field]; - if (v == '') return; - let [h,m,s] = v.split(':') - date.setHours(Number(h)); - date.setMinutes(Number(m)); - date.setSeconds(0); - this[field] = date; }]) - this.get_callback_list(`--${field}-date`).push( - [el, (el, v) => { let date = this[field]; - if (v == '') return; - let [y,m,d] = v.split('-') - date.setYear(Number(y)/* - 1900*/); - date.setMonth(Number(m) - 1); - date.setDate(d); - this[field] = date; }]) - - - /* Manual fetch of the fields instead of the general method, - to avoid an infinite loop of dtstart setting --dtstart-time, - and vice versa. - NOTE if many more fields require special treatment then a - general solution is required. - */ - let l = this.get_callback_list(field); - console.log(l); - l.push( - [el, (el, v) => { popup - .querySelector(`.edit-tab input[name='${field}-time']`) - .value = v.format("~H:~M"); - popup - .querySelector(`.edit-tab input[name='${field}-date']`) - .value = v.format("~Y-~m-~d"); - }]); - console.log(l); - } - /* Popuplate default types, see types.js for property_names */ for (let property of property_names) { this.ical_properties.add(property) -- cgit v1.2.3