aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--module/calp/html/view/calendar/week.scm153
-rw-r--r--static/_global.scss11
-rw-r--r--static/components/popup-element.ts86
-rw-r--r--static/components/tab-element.ts28
-rw-r--r--static/dragable.ts4
-rw-r--r--static/elements.ts2
-rw-r--r--static/style.scss218
7 files changed, 243 insertions, 259 deletions
diff --git a/module/calp/html/view/calendar/week.scm b/module/calp/html/view/calendar/week.scm
index e13a0ae3..b228991b 100644
--- a/module/calp/html/view/calendar/week.scm
+++ b/module/calp/html/view/calendar/week.scm
@@ -2,6 +2,7 @@
:use-module (calp util)
:use-module (srfi srfi-1)
:use-module (srfi srfi-41)
+ :use-module (rnrs records syntactic)
:use-module (datetime)
:use-module (calp html view calendar shared)
:use-module (calp html config)
@@ -14,9 +15,8 @@
events-between))
:use-module ((calp html vcomponent)
:select (make-block) )
- :use-module ((calp html components)
- :select (btn tabset ; form with-label
- ))
+ ;; :use-module ((calp html components)
+ ;; :select ())
:use-module ((vcomponent group)
:select (group-stream get-groups-between))
)
@@ -86,73 +86,86 @@
,(vevent-edit-rrule-template))
;; Based on popup:s output
- (template
- (@ (id "popup-template"))
- (div (@ ; (id ,id)
- (class "popup-container")
- (onclick "event.stopPropagation()"))
- (div (@ (class "popup"))
- (nav (@ (class "popup-control"))
- ,(btn "×"
- title: "Stäng"
- onclick: ""
- class: '("close-button"))
- ,(btn "🗑"
- title: "Ta bort"
- class: '("remove-button")
- onclick: ""))
-
- (div (@ (class "tabgroup"))
- (tab-element
- (@ (label-title "Översikt")
- (label "📅"))
- (vevent-description
- (@(class "vevent populate-with-uid"))))
- (tab-element
- (@ (label-title "Redigera")
- (label "🖊"))
- (vevent-edit (@ (class "populate-with-uid"))))
- (tab-element
- (@ (label-tile "Upprepningar")
- (label "↺"))
- (vevent-edit-rrule (@ (class "populate-with-uid"))))
- ,@(when (debug)
- `((tab-element
- (@ (label-title "Debug")
- (label "🐸"))
- (vevent-dl (@ (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)))
- (div (@ (class "content"))
- (slot (@ (name "content"))
- (span (@ (class "error"))
- "CONTENT MISSING")))))
- )))
+ (template (@ (id "popup-template"))
+ ,(popup-template)))))
+
+
+(define-record-type tab
+ (fields title label body))
+
+(define (popup-template)
+
+ ;; the XXX-n and YYY-n id:s aren't actually used, but mearly show how things
+ ;; are supposed to be linked together.
+ ;; Each instance of XXX should be replaced with THE SAME unique id,
+ ;; and each instance of YYY shoud be replaced with another, but unique id.
+ ;; n is a serial number, where a tab and its label MUST have the same number.
+
+ (define* (build-tab
+ tabdata key:
+ (selected "false")
+ (tabindex "-1"))
+ `(button (@ (role "tab")
+ (aria-selected ,selected)
+ (tabindex ,tabindex)
+ (aria-controls "XXX-n")
+ (id "YYY-n")
+ (title ,(tab-title tabdata)))
+ ,(tab-label tabdata)))
+
+ (define tabs
+ (append
+ (list
+ (make-tab "Översikt" "📅"
+ '(vevent-description
+ (@ (class "vevent populate-with-uid"))))
+ (make-tab "Redigera" "🖊"
+ '(vevent-edit (@ (class "populate-with-uid"))))
+ (make-tab "Upprepningar" "↺"
+ '(vevent-edit-rrule (@ (class "populate-with-uid")))))
+
+ (when (debug)
+ (list
+ (make-tab "Debug" "🐸"
+ '(vevent-dl (@ (class "populate-with-uid"))))
+ ))))
+
+
+ ;; becomes the direct child of <popup-element/>
+ `(div (@ (class "popup-root window")
+ (onclick "event.stopPropagation()"))
+
+ (nav (@ (class "popup-control"))
+ (button (@ (class "close-button")
+ (title "Stäng")
+ (aria-label "Close"))
+ "×")
+ (button (@ (class "remove-button")
+ (title "Ta Bort"))
+ "🗑"))
+
+ (main (@ (class "tabgroup window-body"))
+ (menu (@ (role "tablist")
+ (aria-label "Simple Tabs"))
+ ,@(cons (build-tab (car tabs)
+ selected: "true"
+ tabindex: "0")
+ (map build-tab (cdr tabs))))
+ ;; content
+ (article (@ (id "XXX-n")
+ (role "tabpanel")
+ (tabindex "0")
+ (aria-labeledby "YYY-n"))
+ ,(tab-body (car tabs)))
+ ,@(map (lambda (tab)
+ `(article (@ (id "XXX-n")
+ (role "tabpanel")
+ (tabindex "0")
+ (hidden)
+ (aria-labeledby "YYY-n"))
+ ,(tab-body tab)))
+ (cdr tabs))
+ )))
(define (week-day-select args)
`(select (@ ,@args)
diff --git a/static/_global.scss b/static/_global.scss
index 8a5bee83..41f426f9 100644
--- a/static/_global.scss
+++ b/static/_global.scss
@@ -1,5 +1,16 @@
$gray: #757575;
$blue: #3399ff;
+/* TODO rename this */
$btn-height: 0.5ex;
+$tablabel-height: 5ex;
+$tablabel-margin: 0;
+// "left" or "top"
+$popup-style: "left";
+
+:root {
+ /* Each popup can have a different amoutn of tabs.
+ Override this as appropriate */
+ --tabcount: 4;
+}
diff --git a/static/components/popup-element.ts b/static/components/popup-element.ts
index cb67035f..e26ae578 100644
--- a/static/components/popup-element.ts
+++ b/static/components/popup-element.ts
@@ -7,7 +7,6 @@ import { close_popup, event_from_popup } from '../popup'
import { vcal_objects } from '../globals'
import { ComponentVEvent } from './vevent'
-import { TabElement } from './tab-element'
import { remove_event } from '../server_connect'
@@ -47,28 +46,58 @@ class PopupElement extends ComponentVEvent {
let body = (template.content.cloneNode(true) as DocumentFragment).firstElementChild!;
let uid = this.uid;
- // console.log(uid);
body.getElementsByClassName('populate-with-uid')
.forEach((e) => e.setAttribute('data-uid', uid));
- /* tabs */
- // 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
+
+ /* tab change button */
+ let tabs = this.querySelectorAll('[role="tab"]')
+ /* list of all tabs */
+ // let tablist = this.querySelector('[role="tablist"]')!
+
+ tabs.forEach(tab => {
+ tab.addEventListener('click', () => {
+
+ /* hide all tab panels */
+ for (let tabcontent of this.querySelectorAll('[role="tabpanel"]')) {
+ tabcontent.setAttribute('hidden', 'true');
+ }
+ /* unselect all (selected) tab handles */
+ for (let item of this.querySelectorAll('[aria-selected="true"]')) {
+ item.setAttribute('aria-selected', 'false');
+ }
+ /* re-select ourselves */
+ tab.setAttribute('aria-selected', 'true');
+
+ /* unhide our target tab */
+ this.querySelector('#' + tab.getAttribute('aria-controls'))!
+ .removeAttribute('hidden')
+ });
+ });
+
+ /* tab contents */
+ let tabcontents = this.querySelectorAll('[role="tabpanel"]')
+
+ for (let i = 0; i < tabs.length; i++) {
+ let n = i + this.tabcount;
+ this.tabgroup_id
+ let tab = tabs[n];
+ let con = tabcontents[n];
+
+ let a = `${this.tabgroup_id}-tab-${n}`
+ let b = `${this.tabgroup_id}-con-${n}`
+
+ tab.id = a;
+ con.setAttribute('aria-labeledby', a);
+
+ con.id = b;
+ tab.setAttribute('aria-controls', b);
+
}
- (this.querySelector('tab-element label') as HTMLInputElement).click()
+ this.tabcount += tabs.length
+
});
/* end tabs */
@@ -126,27 +155,4 @@ class PopupElement extends ComponentVEvent {
this.style.left = offsetX + "px";
this.style.top = offsetY + "px";
}
-
- addTab(tab: TabElement) {
- let tabgroup = this.getElementsByClassName('tabgroup')![0]!
- tabgroup.append(tab);
- popuplateTab(tab, this.tabgroup_id, this.tabcount)
- this.tabcount += 1
- }
-}
-
-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...? */
- if (input !== null) {
- input.name = tabgroup
- input.id = new_id;
- }
- if (label !== null) {
- label.setAttribute('for', new_id);
- }
}
diff --git a/static/components/tab-element.ts b/static/components/tab-element.ts
deleted file mode 100644
index 9da6c504..00000000
--- a/static/components/tab-element.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-export { TabElement }
-
-/* <tab-element /> */
-class TabElement extends HTMLElement {
- constructor() {
- super();
- }
-
- connectedCallback() {
- let template
- = (document.getElementById('tab-template') as HTMLTemplateElement)
- .content
- // const shadowRoot = this.attachShadow({ mode: 'open' })
- // .appendChild(template.cloneNode(true));
-
- let content = Array.from(this.children, (e) => e.cloneNode(true))
-
- this.replaceChildren(template.cloneNode(true));
-
- let label = this.querySelector('label')
- if (!label) throw "Invalid tab"
-
- label.setAttribute('title', this.getAttribute('label-title') || '')
- label.innerText = this.getAttribute('label') || 'T'
-
- this.querySelector('slot[name="content"]')!.replaceWith(...content);
- }
-}
diff --git a/static/dragable.ts b/static/dragable.ts
index 6110a510..b32bb608 100644
--- a/static/dragable.ts
+++ b/static/dragable.ts
@@ -18,7 +18,7 @@ function bind_popup_control(nav: HTMLElement) {
// throw TypeError('not a popup container');
// }
- nav.onmousedown = function(e) {
+ nav.addEventListener('mousedown', function(e) {
/* Ignore mousedown on children */
if (e.target != nav) return;
nav.style.cursor = "grabbing";
@@ -27,7 +27,7 @@ function bind_popup_control(nav: HTMLElement) {
// let popup = nav.closest(".popup-container");
let popup = nav.closest("popup-element") as HTMLElement;
nav.dataset.startPoint = popup.offsetLeft + ";" + popup.offsetTop;
- }
+ })
window.addEventListener('mousemove', function(e) {
if (nav.dataset.grabbed) {
let [x, y] = nav.dataset.grabPoint!.split(";").map(Number);
diff --git a/static/elements.ts b/static/elements.ts
index db834fd9..b499556f 100644
--- a/static/elements.ts
+++ b/static/elements.ts
@@ -4,7 +4,6 @@ import { VEventDL } from './components/vevent-dl'
import { ComponentBlock } from './components/vevent-block'
import { DateTimeInput } from './components/date-time-input'
import { PopupElement } from './components/popup-element'
-import { TabElement } from './components/tab-element'
import { InputList } from './components/input-list'
import { EditRRule } from './components/edit-rrule'
@@ -30,5 +29,4 @@ function initialize_components() {
/* These maybe also require that the global maps are initialized */
customElements.define('popup-element', PopupElement)
- customElements.define('tab-element', TabElement)
}
diff --git a/static/style.scss b/static/style.scss
index 9833a77a..64098283 100644
--- a/static/style.scss
+++ b/static/style.scss
@@ -24,7 +24,10 @@ html, body {
/* main the graphical portion
of both the wide and the table
view */
- main {
+ > main {
+ /* these allow the main area to shrink, so that all areas will fit the
+ screen. It will however not shrink the elements, leading to our
+ (desired) scrollbar */
min-width: 0; /* for wide */
min-height: 0; /* for tall */
@@ -742,7 +745,7 @@ vevent-block, .event {
.error {
border: 3px solid red;
background-color: pink;
-
+
pre {
padding: 1em;
}
@@ -766,157 +769,126 @@ popup-element {
&.visible {
display: block;
}
-}
-.popup {
- display: flex;
- background-color: #dedede;
- color: black;
- font-size: 80%;
+ main {
+ resize: both;
+ overflow: auto;
- /* overflow-y: auto; */
- max-width: 60ch;
- max-height: 60ch;
- min-width: 60ch;
- min-height: 30ch;
+ min-height: calc(var(--tabcount) * #{$tablabel-margin + $tablabel-height});
- select {
- max-width: 100%;
- }
+ /* some form of sensible minimi and default size for the popup (s main area). */
+ min-width: 150px;
+ width: 350px;
+ height: 250px;
- input {
- white-space: initial;
- border: 1px solid gray;
- max-width: 100%;
- }
-
- .eventtext {
- /* makes the text in the popup scroll, but not the sidebar */
- overflow-y: auto;
- padding: 1em;
- word-break: break-word;
-
- table {
- word-break: initial;
- font-size: 65%;
+ article {
+ padding: 1em;
}
}
+}
- .location {
- font-style: initial;
- }
+.popup-control {
+ cursor: grab;
+ background-color: var(--color);
- .category {
- display: inline-block;
- margin-right: 1ex;
- }
+ display: flex;
- .popup-control {
- display: flex;
+ @if $popup-style == "left" {
flex-direction: column;
+ padding: 1.2ex;
+ } @else {
+ flex-direction: row-reverse;
+ padding: 1ex;
+ }
- /* not needed, but the icons aren't text
- and should therefor not be copied */
- user-select: none;
-
- cursor: grab;
- background-color: var(--color);
- /* Transition for background color
- * Matches that of '.event'.
- * TODO break out to common place */
- transition: 0.3s;
-
- .btn {
- max-width: 2em;
- max-height: 2em;
- margin: 1em;
- display: flex;
- align-items: center;
- justify-content: center;
+ button {
+ display: block;
+ background: $blue;
+ color: white;
+ border: none;
+ box-shadow: $btn-height $btn-height gray;
- font-size: 150%;
+ &:active {
+ transform: translate($btn-height, $btn-height);
+ box-shadow: none;
}
+ @if $popup-style == "left" {
+ width: 9mm;
+ height: 9mm;
+ margin-bottom: 2mm;
+ } @else {
+ width: 7mm;
+ height: 7mm;
+ margin-left: 1mm;
+ }
}
}
+.popup-root {
+ background-color: #dedede;
+ color: black;
-#bar {
- width: calc(100% + 2em);
- height: 4px;
- background: blue;
- border-color: blue;
- left: -1em;
-}
+ display: flex;
-/* Tabs
-----------------------------------------
-*/
+ @if $popup-style == "left" {
+ flex-direction: row;
+ } @else {
+ flex-direction: column;
+ }
-.tabgroup {
- position: relative;
- width: 100%;
- resize: both;
- --tab-size: 6ex;
-}
-.tab {
- > label {
+ [role="tablist"] {
+ display: flex;
+ flex-direction: column;
position: absolute;
- top: calc(var(--tab-size) * var(--tab-index));
-
left: 100%;
- /*top: 0; */
- display: block;
+ margin: 0;
+ padding: 0;
- max-height: 5ex;
- min-height: 5ex;
+ [role="tab"] {
+ height: $tablabel-height;
+ margin-bottom: $tablabel-margin;
- min-width: 5ex;
- width: 5ex;
+ width: 5ex;
+ &:hover {
+ width: 10ex;
+ }
- transition: width 0.1s ease-in-out;
- &:hover {
- width: 10ex;
+ transition: width 0.1s ease-in-out;
+ border: 1px solid #ccc;
+ border-radius: 0 5px 5px 0;
+ background-color: #aeaeae;
}
- border: 1px solid #ccc;
- border-radius: 0 5px 5px 0;
- background-color: #aeaeae;
-
- display: flex;
- justify-content: center;
- align-items: center;
- }
-
- [type=radio] {
- display: none;
- &:checked ~ label {
- z-index: 100;
- /* to align all tab */
- border-left: 3px solid transparent;
+ [aria-selected="true"] {
+ border-left: none;
background-color: #dedede;
-
- ~ .content {
- z-index: 100;
- }
}
}
+}
- .content {
- position: absolute;
- top: 0;
- left: 0;
- background-color: #dedede;
- right: 0;
- bottom: 0;
- overflow: auto;
+vevent-edit {
- min-width: 100%;
- min-height: 100%;
+ select {
+ max-width: 100%;
+ }
+
+ input {
+ white-space: initial;
+ border: 1px solid gray;
+ max-width: 100%;
}
+ .eventtext {
+ word-break: break-word;
+
+ table {
+ word-break: initial;
+ font-size: 65%;
+ }
+ }
.edit-form {
label {
@@ -936,10 +908,22 @@ popup-element {
.timeinput {
}
}
+}
+
+vevent-dl {
+ font-size: 80%;
+}
+#bar {
+ width: calc(100% + 2em);
+ height: 4px;
+ background: blue;
+ border-color: blue;
+ left: -1em;
}
+
.plusminuschecked label {
color: black;
}