diff options
Diffstat (limited to 'static/components/popup-element.ts')
-rw-r--r-- | static/components/popup-element.ts | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/static/components/popup-element.ts b/static/components/popup-element.ts new file mode 100644 index 00000000..35c966ac --- /dev/null +++ b/static/components/popup-element.ts @@ -0,0 +1,198 @@ +export { PopupElement, setup_popup_element } + +import { VEvent } from '../vevent' +import { find_block, vcal_objects } from '../globals' + +import { ComponentVEvent } from './vevent' + +import { remove_event } from '../server_connect' + +/* <popup-element /> */ +class PopupElement extends ComponentVEvent { + + /* The popup which is the "selected" popup. + /* Makes the popup last hovered over the selected popup, moving it to + * the top, and allowing global keyboard bindings to affect it. */ + static activePopup: PopupElement | null = null; + + constructor(uid?: string) { + super(uid); + + /* TODO populate remaining (??) */ + + let obj = vcal_objects.get(this.uid); + if (obj && obj.calendar) { + this.dataset.calendar = obj.calendar; + } + + /* Makes us the active popup */ + this.addEventListener('mouseover', () => { + if (PopupElement.activePopup) { + PopupElement.activePopup.removeAttribute('active'); + } + PopupElement.activePopup = this; + this.setAttribute('active', 'active'); + }) + } + + redraw(data: VEvent) { + if (data.calendar) { + /* The CSS has hooks on [data-calendar], meaning that this can + (and will) change stuff */ + this.dataset.calendar = data.calendar; + } + + } + + connectedCallback() { + let template = document.getElementById('popup-template') as HTMLTemplateElement + let body = (template.content.cloneNode(true) as DocumentFragment).firstElementChild!; + + let uid = this.uid; + + /* nav bar */ + let nav = body.getElementsByClassName("popup-control")[0] as HTMLElement; + bind_popup_control(nav); + + let close_btn = body.querySelector('.popup-control .close-button') as HTMLButtonElement + close_btn.addEventListener('click', () => this.visible = false); + + let maximize_btn = body.querySelector('.popup-control .maximize-button') as HTMLButtonElement + maximize_btn.addEventListener('click', () => this.maximize()); + + let remove_btn = body.querySelector('.popup-control .remove-button') as HTMLButtonElement + remove_btn.addEventListener('click', () => remove_event(uid)); + /* end nav bar */ + + this.replaceChildren(body); + } + + static get observedAttributes() { + return ['visible']; + } + + attributeChangedCallback(name: string, oldValue?: string, newValue?: string) { + switch (name) { + case 'visible': + this.onVisibilityChange() + break; + } + } + + get visible(): boolean { + return this.hasAttribute('visible'); + } + + set visible(isVisible: boolean) { + if (isVisible) { + this.setAttribute('visible', 'visible'); + } else { + this.removeAttribute('visible'); + } + } + + private onVisibilityChange() { + + /* TODO better way to find root */ + let root; + switch (window.VIEW) { + case 'week': + root = document.getElementsByClassName("days")[0]; + break; + case 'month': + default: + root = document.body; + break; + } + + let element = find_block(this.uid) as HTMLElement | null + /* start <X, Y> sets offset between top left corner + of event in calendar and popup. 10, 10 soo old + event is still visible */ + let offsetX = 10, offsetY = 10; + while (element !== root && element !== null) { + offsetX += element.offsetLeft; + offsetY += element.offsetTop; + element = element.offsetParent as HTMLElement; + } + this.style.left = offsetX + "px"; + this.style.top = offsetY + "px"; + + /* Reset width and height to initial, to save user if they have resized + it to something weird */ + let el = this.firstElementChild as HTMLElement; + el.style.removeProperty('width'); + el.style.removeProperty('height'); + } + + maximize() { + /* TODO this assumes that popups are direct decendant of their parent, + which they really ought to be */ + let parent = this.parentElement!; + let el = this.firstElementChild as HTMLElement + /* TODO offsetParent.scrollLeft places us "fullscreen" according to the currently + scrolled viewport. But is this the correct way to do it? How does it work for + month views */ + this.style.left = `${this.offsetParent!.scrollLeft + 10}px`; + this.style.top = '10px'; + /* 5ex is width of tab labels */ + el.style.width = `calc(${parent.clientWidth - 20}px - 5ex)` + el.style.height = `${parent.clientHeight - 20}px` + } +} + +/* Create a new popup element for the given VEvent, and ready it for editing the + event. Used when creating event (through the frontend). + The return value can safely be ignored. +*/ +function setup_popup_element(ev: VEvent): PopupElement { + let uid = ev.getProperty('uid'); + let popup = new PopupElement(uid); + ev.register(popup); + /* TODO propper way to find popup container */ + (document.querySelector('.days') as Element).appendChild(popup); + let tabBtn = popup.querySelector('[role="tab"][title="Redigera"]') as HTMLButtonElement + tabBtn.click() + let tab = document.getElementById(tabBtn.getAttribute('aria-controls')!)! + let input = tab.querySelector('input[name="summary"]') as HTMLInputElement + popup.visible = true; + input.select(); + return popup; +} + +/* + Given the navbar of a popup, make it dragable. + */ +function bind_popup_control(nav: HTMLElement) { + + // if (!nav.closest('popup-element')) { + // console.log(nav); + // throw TypeError('not a popup container'); + // } + + nav.addEventListener('mousedown', function(e) { + /* Ignore mousedown on children */ + if (e.target != nav) return; + nav.style.cursor = "grabbing"; + nav.dataset.grabbed = "true"; + nav.dataset.grabPoint = e.clientX + ";" + e.clientY; + // 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); + let [startX, startY] = nav.dataset.startPoint!.split(";").map(Number); + // let popup = nav.closest(".popup-container"); + let popup = nav.closest("popup-element") as HTMLElement; + + popup.style.left = startX + (e.clientX - x) + "px"; + popup.style.top = startY + (e.clientY - y) + "px"; + } + }); + window.addEventListener('mouseup', function() { + nav.dataset.grabbed = ""; + nav.style.cursor = ""; + }); +} |