-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathSolarPopup.ts
111 lines (95 loc) · 3.79 KB
/
SolarPopup.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { ModalBackground } from "./ModalBackground"
import { IComponent, KeyCodes, OptionalDimensions } from "vanilla-typescript"
import "./SolarPopup.pcss"
import { constants } from "./constants"
/**
* A Popup that can take any content
*
* Features
* Closes in response to [ESC] keypress, submit events.
* Adds modal background that fades in with CSS3 transitions
* Popup itself slides in with CSS3 transitions
*
* If you specify fixed Dimensions then it will explicitly set the width and height optionally
*
* @constructor
*/
export class SolarPopup implements IComponent {
destroyBoundWithThis = this.destroy.bind(this)
modalBackground = new ModalBackground()
hostElement: HTMLElement
keyupHandler = event => {
if (event.keyCode === KeyCodes.ESC) {
this.destroyBoundWithThis()
}
}
constructor(child: HTMLElement, optionalDimensions?: OptionalDimensions) {
const tempElement: HTMLElement = document.createElement("DIV")
tempElement.innerHTML = `<article class='solar-popup' data-is-initialising='true'>
<a class='close'><!--❌-->✖</a>
<div class='childContainer'></div>
</article>`
this.hostElement = tempElement.firstChild as HTMLElement
this.hostElement.querySelector(".childContainer").appendChild(child)
var htmlStyles = window.getComputedStyle(document.querySelector("html"))
var popupWidth = htmlStyles.getPropertyValue("--popup-width") // todo, use to center popup
if (optionalDimensions) {
if (window.innerWidth > 2 * 10 + optionalDimensions.width) {
document.documentElement.style.setProperty("--popup-width", optionalDimensions.width + "px")
}
if (window.innerHeight > 2 * 10 + optionalDimensions.height) {
// todo adjust for small height
this.hostElement.style.height = `${optionalDimensions.height}px`
}
}
}
/**
* Shows
* @param {Element} child we need to keep the reference to keep custom functionality in the child
*/
show(): Promise<void> {
document.body.appendChild(this.hostElement)
// let currentWidth = window.getComputedStyle(document.querySelector('p'))
this.modalBackground.render()
return new Promise((resolve, reject) => {
// we need to set this in a timeout in order to trigger the css transition
setTimeout(() => {
this.hostElement.dataset["isInitialising"] = "false"
})
// when the popup is has finished moving via the css transition resolve the promise to tell the callee
setTimeout(() => {
this.addListeners()
resolve()
}, constants.TRANSITION_TIMES)
})
}
addListeners() {
const closeElement = this.hostElement.querySelector("a")
closeElement.addEventListener("click", this.destroyBoundWithThis)
this.hostElement.classList.remove("offscreen")
document.addEventListener("keyup", this.keyupHandler)
this.hostElement.addEventListener("submit", event => {
this.destroyBoundWithThis()
event.preventDefault()
})
// handle the first child submit button click, close popup by default
// this is a convention that gets popup to behave in sensible way
const submitBtn = this.hostElement.querySelector('button[type="submit"]')
if (submitBtn) {
submitBtn.addEventListener("click", this.destroyBoundWithThis)
}
}
destroy(): Promise<any> {
// visual indicator for this element and delegate to the modal
this.hostElement.dataset["isDestructing"] = "true"
this.modalBackground.destroy()
return new Promise(resolve => {
setTimeout(() => {
// previous popup won't be removed from garbage collector in time
document.removeEventListener("keyup", this.keyupHandler)
this.hostElement.parentElement.removeChild(this.hostElement)
resolve()
}, constants.TRANSITION_TIMES)
})
}
}