aboutsummaryrefslogtreecommitdiff
path: root/static
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2021-11-21 16:08:08 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2021-11-21 16:25:59 +0100
commite7d80fcfa91f92c712110d58151df0f8f1e6ed86 (patch)
tree9bc424729c8d9e6ef65d959cc0bfe7cc0f9145e8 /static
parentAdd basic rrule tab. (diff)
downloadcalp-e7d80fcfa91f92c712110d58151df0f8f1e6ed86.tar.gz
calp-e7d80fcfa91f92c712110d58151df0f8f1e6ed86.tar.xz
Rework popup components.
Previously popups were driven through some CSS hacks, which used labels with specific positioning, and z-index changes. This never really worked, and led the rest of the tree to be unmanagable. This commit replaces that system with a simpler one, which is being driven by javascript. This also allowed a much simpler tree, which allowed us to - make the popups rezisable (with a resize anchor) - move the window handle to above (configurable) - Add and remove tabs without having manually reflow where all labels are
Diffstat (limited to 'static')
-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
6 files changed, 160 insertions, 189 deletions
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;
}