From 4f499ccbf71d0ae662159515bb568826e72678a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Wed, 6 Oct 2021 04:32:11 +0200 Subject: Drive popup tabs through javascript. --- module/calp/html/vcomponent.scm | 34 +++++++++ module/calp/html/view/calendar/week.scm | 51 +++++++++++--- static/globals.ts | 120 ++++++++++++++++++++++++++++---- static/style.scss | 3 +- static/vevent.ts | 4 ++ 5 files changed, 190 insertions(+), 22 deletions(-) diff --git a/module/calp/html/vcomponent.scm b/module/calp/html/vcomponent.scm index 09d0038b..d1cd4886 100644 --- a/module/calp/html/vcomponent.scm +++ b/module/calp/html/vcomponent.scm @@ -316,6 +316,40 @@ (input (@ (type "submit"))) ))) +;; (define-public (property-input-template) +;; (div (@ (class "")) +;; (input (@ (type "text") +;; (name "name") +;; (list "known-fields") +;; (placeholder "Nytt fält"))) +;; (select (@ (name "type")) +;; (option (@ (value "TEXT")) "Text")) +;; (span +;; (input (@ (type "text") +;; (name "value") +;; (placeholder "Värde")))))) + +;; (define (list-input-template) +;; ;; It would be better if these input-list's worked on the same +;; ;; class=bind system as the fields above. The problem with that +;; ;; is however that each input-list requires different search +;; ;; and join procedures. Currently this is bound in the JS, see +;; ;; [CATEGORIES_BIND]. +;; ;; It matches on ".input-list[data-property='categories']". +;; `(div (@ (class "input-list") +;; (data-property "categories")) +;; #; +;; ,@(awhen (prop ev 'CATEGORIES) ; +;; (map (lambda (c) ; +;; `(input (@ (size 2) ; +;; (class "unit") ; +;; (value ,c)))) ; +;; it)) + +;; (input (@ (class "unit final") +;; (size 2) +;; (type "text") +;; )))) ;; Single event in side bar (text objects) (define-public (fmt-day day) diff --git a/module/calp/html/view/calendar/week.scm b/module/calp/html/view/calendar/week.scm index 17bb3b2d..388c2f07 100644 --- a/module/calp/html/view/calendar/week.scm +++ b/module/calp/html/view/calendar/week.scm @@ -99,18 +99,51 @@ ;; onclick: "close_popup(document.getElementById(this.closest('.popup-container').id))" class: '("close-tooltip"))) - ,(tabset - `(("📅" title: "Översikt" - (vevent-description - (@ (class "vevent populate-with-uid"))) - ) + (div (@ (class "tabgroup")) + (tab-element + (@ (title "Översikt")) + (span (@ (slot "label")) "📅") + (vevent-description + (@ (slot "content") + (class "vevent populate-with-uid")))) + (tab-element + (@ (title "Redigera")) + (span (@ (slot "label")) "📅") + (vevent-edit (@ (slot "content") + (class "populate-with-uid"))) + )) - ,@(when (edit-mode) - `(("📅" title: "Redigera" - (vevent-edit (@ (class "populate-with-uid")))))) + ;; ,(tabset + ;; `(("📅" title: "Översikt" + ;; (vevent-description + ;; (@ (class "vevent populate-with-uid"))) + ;; ) - ))))) + ;; ,@(when (edit-mode) + ;; `(("📅" title: "Redigera" + ;; (vevent-edit (@ (class "populate-with-uid")))))) + ;; )) + ))) + + (template + (@ (id "tab-template")) + ;; ,((@ (calp html components) include-css) "/static/tab.css") + (div (@ (class "tab")) + (input (@ (type "radio") + ;; id + ;; (name ,tabgroup) + )) + (label (@ ; for id + ;; style= top: calc(var(--tab-size) * i) + (title ; title + )) + (slot (@ (name "label")) "??") + ) + (div (@ (class "content")) + (slot (@ (name "content")) + (span (@ (class "error")) + "CONTENT MISSING"))))) ))) ;; based on the output of fmt-single-event diff --git a/static/globals.ts b/static/globals.ts index 0df1aabd..970de8f7 100644 --- a/static/globals.ts +++ b/static/globals.ts @@ -63,6 +63,18 @@ class ComponentDescription extends ComponentVEvent { } +function popuplateTab(tab: HTMLElement, tabgroup: string, index: number) { + // console.log(tab); + let new_id = gensym(); + let input = tab.querySelector('input[type="radio"]') as HTMLInputElement; + let label = tab.querySelector("label")! + tab.style.setProperty('--tab-index', '' + index); + /* TODO this throws a number of errors, but somehow still works...? */ + input.name = tabgroup + input.id = new_id; + label.setAttribute('for', new_id); +} + class ComponentEdit extends ComponentVEvent { firstTime: boolean @@ -246,7 +258,7 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement { constructor() { super(); this.innerHTML = '' - console.log('constructing datetime input') + // console.log('constructing datetime input') } static get observedAttributes() { @@ -254,7 +266,7 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement { } attributeChangedCallback(name: string, from: any, to: any) { - console.log(this, name, boolean(from), boolean(to)); + // console.log(this, name, boolean(from), boolean(to)); switch (name) { case 'dateonly': (this.querySelector('input[type="time"]') as HTMLInputElement) @@ -290,7 +302,7 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement { } set value(new_value: Date | string) { - console.log('Setting date'); + // console.log('Setting date'); let date, time; if (new_value instanceof Date) { date = new_value.format("~L~Y-~m-~d"); @@ -314,16 +326,67 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement { customElements.define('date-time-input', DateTimeInput /*, { extends: 'input' } */) +function verifySlot(el: Node | null): el is HTMLElement { + if (el === null) { + console.error("Element is null"); + return false; + } + if (!(el instanceof HTMLElement)) { + console.error("Node is not an HTMLElement", el); + return false; + } + return true +} + +class TabElement extends HTMLElement { + constructor() { + super(); + } + + connectedCallback() { + // this.replaceChildren(template.cloneNode(true)); + let template + = (document.getElementById('tab-template') as HTMLTemplateElement) + .content + // const shadowRoot = this.attachShadow({ mode: 'open' }) + // .appendChild(template.cloneNode(true)); + // console.log(this); + let label = this.querySelector('[slot="label"]') + let content = this.querySelector('[slot="content"]') + if (!verifySlot(label)) throw "Bad label"; + if (!verifySlot(content)) throw "Bad content"; + + this.replaceChildren(template.cloneNode(true)); + this.querySelector('slot[name="label"]')!.replaceWith(label); + this.querySelector('slot[name="content"]')!.replaceWith(content); + } +} + +function buildDescriptionList(data: [string, any][]): HTMLElement { + let dl = document.createElement('dl'); + for (let [key, val] of data) { + dl.appendChild(makeElement('dt', { innerText: key })) + dl.appendChild(makeElement('dd', { innerText: val })) + } + return dl; +} + class PopupElement extends HTMLElement { + + tabgroup_id: string + tabcount: number + constructor() { super(); /* TODO populate remaining */ // this.id = 'popup' + this.dataset.uid + this.tabgroup_id = gensym(); + this.tabcount = 0 } redraw() { - console.log('IMPLEMENT ME'); + console.warn('IMPLEMENT ME'); } connectedCallback() { @@ -340,14 +403,23 @@ class PopupElement extends HTMLElement { .forEach((e) => e.setAttribute('data-uid', uid)); /* tabs */ - let tabgroup_id = gensym(); - for (let tab of body.querySelectorAll(".tabgroup .tab")) { - let new_id = gensym(); - let input = tab.querySelector("input")!; - input.id = new_id; - input.name = tabgroup_id; - tab.querySelector("label")!.setAttribute('for', new_id); - } + // for (let tab of body.querySelectorAll(".tabgroup .tab")) { + // } + window.setTimeout(() => { + // let tabs = this.querySelector('tab-element')! + // .shadowRoot! + // .querySelectorAll('label') + // console.log(tabs); + // console.log(this.getElementsByTagName('tab-element')) + for (let tab of this.getElementsByTagName('tab-element')) { + // console.log(tab_container); + // let tab = tab_container.shadowRoot!; + // tab.documentElement.style.setProperty('--i', i); + popuplateTab(tab as TabElement, this.tabgroup_id, this.tabcount) + this.tabcount += 1 + } + (this.querySelector('tab-element label') as HTMLInputElement).click() + }); /* end tabs */ /* nav bar */ @@ -370,11 +442,35 @@ class PopupElement extends HTMLElement { // event.properties.calendar = this.value; }); + + + let tab = makeElement('tab-element', { title: 'Debug' }) as TabElement + /// let tab = new TabElement(); + tab.setAttribute('title', 'Debug') + tab.appendChild(makeElement('span', { slot: 'label', innerText: 'D' })) + // let dl = makeElement('dl', { slot: 'content' }) + let obj = vcal_objects.get(uid)! + let dl = buildDescriptionList( + Array.from(obj.boundProperties) + .map(key => [key, obj.getProperty(key)])) + dl.slot = 'content' + tab.appendChild(dl) + + this.addTab(tab); + // window.setTimeout(() => { this.addTab(tab) }) + } + + addTab(tab: TabElement) { + let tabgroup = this.getElementsByClassName('tabgroup')![0]! + tabgroup.append(tab); + popuplateTab(tab, this.tabgroup_id, this.tabcount) + this.tabcount += 1 } } window.addEventListener('load', function() { customElements.define('popup-element', PopupElement) + customElements.define('tab-element', TabElement) }); function wholeday_checkbox(box: HTMLInputElement) { diff --git a/static/style.scss b/static/style.scss index cc1ae15b..56acd18c 100644 --- a/static/style.scss +++ b/static/style.scss @@ -856,9 +856,10 @@ popup-element { .tab { > label { position: absolute; + top: calc(var(--tab-size) * var(--tab-index)); left: 100%; - top: 0; + /*top: 0; */ display: block; max-height: 5ex; diff --git a/static/vevent.ts b/static/vevent.ts index 9d6cec88..ade5d222 100644 --- a/static/vevent.ts +++ b/static/vevent.ts @@ -77,6 +77,10 @@ class VEvent { return e.value; } + get boundProperties(): IterableIterator { + return this.properties.keys() + } + setProperty(key: string, value: any) { let e = this.properties.get(key); if (!e) { -- cgit v1.2.3