Skip to content

Commit a618a81

Browse files
committed
alarm function added
1 parent 7bff113 commit a618a81

23 files changed

+1798
-9
lines changed

components/dialogs/Dialog.js

+285
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
// @ts-check
2+
import { Shadow } from '../../event-driven-web-components-prototypes/src/Shadow.js'
3+
4+
/**
5+
* @export
6+
* @class Dialog
7+
* In Progress
8+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog
9+
* @type {CustomElementConstructor}
10+
*/
11+
export default class Dialog extends Shadow() {
12+
constructor (options = {}, ...args) {
13+
super({ importMetaUrl: import.meta.url, ...options }, ...args)
14+
15+
/**
16+
* @param {'show'|'showModal'} [command='show']
17+
*/
18+
this.show = async (command = this.getAttribute('command-show') || 'show-modal') => {
19+
const dialog = await this.dialogPromise
20+
if (this.hasAttribute('close-other-flyout')) this.dispatchEvent(new CustomEvent(this.getAttribute('close-other-flyout') || 'close-other-flyout', { bubbles: true, cancelable: true, composed: true }))
21+
// @ts-ignore
22+
command = command.replace(/-([a-z]{1})/g, (match, p1) => p1.toUpperCase())
23+
this.dispatchEvent(new CustomEvent('no-scroll', { detail: { hasNoScroll: true }, bubbles: true, cancelable: true, composed: true }))
24+
dialog.classList.remove('closed')
25+
// @ts-ignore
26+
dialog[command]()
27+
// @ts-ignore
28+
Array.from(dialog.querySelectorAll('[autofocus]')).forEach(node => node.focus())
29+
}
30+
this.close = async () => {
31+
const dialog = await this.dialogPromise
32+
this.dispatchEvent(new CustomEvent('no-scroll', { bubbles: true, cancelable: true, composed: true }))
33+
dialog.classList.add('closed')
34+
dialog.close()
35+
if (this.hasAttribute('closed-event-name')) this.dispatchEvent(new CustomEvent(this.getAttribute('closed-event-name') || 'dialog-closed-event', { bubbles: true, cancelable: true, composed: true }))
36+
37+
// remove focus-visibility if dialog closes
38+
const dialogButtons = Array.from(this.root.querySelectorAll('ks-a-button, a-button, button'))
39+
if (dialogButtons.length > 0) {
40+
setTimeout(() => {
41+
dialogButtons.forEach((button) => {
42+
button.blur()
43+
})
44+
}, 50)
45+
}
46+
}
47+
48+
this.clickEventListener = async event => {
49+
const dialog = await this.dialogPromise
50+
// click on backdrop
51+
if (!this.hasAttribute('no-backdrop-close') && event.composedPath()[0] === dialog) {
52+
const rect = dialog.getBoundingClientRect()
53+
if (event.clientY < rect.top || event.clientY > rect.bottom || event.clientX < rect.left || event.clientX > rect.right) {
54+
event.stopPropagation()
55+
this.close()
56+
this.dispatchEvent(new CustomEvent(this.getAttribute('backdrop-clicked') || 'backdrop-clicked', { bubbles: true, cancelable: true, composed: true }))
57+
}
58+
}
59+
}
60+
this.showClickEventListener = event => {
61+
event.stopPropagation()
62+
if (event.target.getAttribute('id') === 'show') {
63+
this.show().then(() => {
64+
const dialogHasAutofocusInputElement = this.root.querySelector('a-input[autofocus-helper]')
65+
if (dialogHasAutofocusInputElement) this.setAutofocus()
66+
})
67+
} else {
68+
this.show('showModal').then(() => {
69+
const dialogHasAutofocusInputElement = this.root.querySelector('a-input[autofocus-helper]')
70+
if (dialogHasAutofocusInputElement) this.setAutofocus()
71+
})
72+
}
73+
}
74+
this.closeClickEventListener = event => {
75+
event.stopPropagation()
76+
this.close()
77+
}
78+
this.showEventListener = event => this.show(event.detail.command)
79+
this.closeEventListener = () => this.close()
80+
this.keyupListener = event => {
81+
if (event.key === 'Escape') this.close()
82+
}
83+
84+
/** @type {(any)=>void} */
85+
this.dialogResolve = map => map
86+
/** @type {Promise<HTMLDialogElement>} */
87+
this.dialogPromise = new Promise(resolve => (this.dialogResolve = resolve))
88+
}
89+
90+
connectedCallback () {
91+
this.hidden = true
92+
this.showNodes.forEach(node => (node.style.display = 'none'))
93+
this.closeNodes.forEach(node => (node.style.display = 'none'))
94+
const showPromises = []
95+
if (this.shouldRenderCSS()) showPromises.push(this.renderCSS())
96+
Promise.all(showPromises).then(() => {
97+
if (this.shouldRenderHTML()) showPromises.push(this.renderHTML())
98+
Promise.all(showPromises).then(() => {
99+
this.hidden = false
100+
this.showNodes.forEach(node => (node.style.display = ''))
101+
this.closeNodes.forEach(node => (node.style.display = ''))
102+
if (this.hasAttribute('open')) {
103+
this.show(this.getAttribute('open') || undefined)
104+
if (!this.hasAttribute('open-on-every-connect')) this.removeAttribute('open')
105+
}
106+
})
107+
})
108+
// From web components the event does not bubble up to this host
109+
this.showNodes.forEach(node => node.addEventListener('click', this.showClickEventListener))
110+
this.closeNodes.forEach(node => node.addEventListener('click', this.closeClickEventListener))
111+
this.addEventListener('click', this.clickEventListener)
112+
if (this.getAttribute('show-event-name')) document.body.addEventListener(this.getAttribute('show-event-name'), this.showEventListener)
113+
if (this.getAttribute('close-event-name')) document.body.addEventListener(this.getAttribute('close-event-name'), this.closeEventListener)
114+
document.addEventListener('keyup', this.keyupListener)
115+
this.addEventListener('close-dialog', this.close)
116+
return showPromises
117+
}
118+
119+
disconnectedCallback () {
120+
// From web components the event does not bubble up to this host
121+
this.showNodes.forEach(node => node.removeEventListener('click', this.showClickEventListener))
122+
this.closeNodes.forEach(node => node.removeEventListener('click', this.closeClickEventListener))
123+
this.removeEventListener('click', this.clickEventListener)
124+
if (this.getAttribute('show-event-name')) document.body.removeEventListener(this.getAttribute('show-event-name'), this.showEventListener)
125+
if (this.getAttribute('close-event-name')) document.body.removeEventListener(this.getAttribute('close-event-name'), this.closeEventListener)
126+
document.removeEventListener('keyup', this.keyupListener)
127+
}
128+
129+
/**
130+
* evaluates if a render is necessary
131+
*
132+
* @return {boolean}
133+
*/
134+
shouldRenderCSS () {
135+
return !this.root.querySelector(`${this.cssSelector} > style[_css]`)
136+
}
137+
138+
/**
139+
* evaluates if a render is necessary
140+
*
141+
* @return {boolean}
142+
*/
143+
shouldRenderHTML () {
144+
return !this.dialog
145+
}
146+
147+
/**
148+
* renders the css
149+
*/
150+
renderCSS () {
151+
this.css = /* css */`
152+
:host {
153+
--outline-style: none;
154+
outline: none !important;
155+
position: relative;
156+
${this.hasAttribute('dialog-desktop-height')
157+
? `
158+
--dialog-height: ${this.getAttribute('dialog-desktop-height')};
159+
`
160+
: ''
161+
}
162+
${this.hasAttribute('dialog-mobile-height')
163+
? `
164+
--dialog-mobile-height: ${this.getAttribute('dialog-mobile-height')};
165+
`
166+
: ''
167+
}
168+
}
169+
:host > dialog .sticky {
170+
position: var(--sticky-position, sticky);
171+
top: var(--sticky-top, 0);
172+
}
173+
:host > dialog {
174+
background-color: var(--background-color, canvas);
175+
}
176+
:host > dialog {
177+
cursor: var(--dialog-cursor, auto);
178+
}
179+
:host > dialog::backdrop {
180+
cursor: var(--dialog-backdrop-cursor, pointer);
181+
}
182+
:host > dialog:modal {
183+
height: var(--dialog-height, fit-content);
184+
min-height: var(--dialog-min-height, auto);
185+
}
186+
:host > dialog > input.hided-input {
187+
display:none;
188+
position: absolute;
189+
opacity:0;
190+
z-index: -1;
191+
}
192+
/* Mobile layout */
193+
@media only screen and (max-width: _max-width_) {
194+
:host > dialog:modal {
195+
height: var(--dialog-mobile-height, var(--dialog-height, fit-content));
196+
}
197+
198+
:host > dialog > input.hided-input {
199+
display: block;
200+
position: absolute;
201+
opacity:0;
202+
z-index: -1;
203+
}
204+
}
205+
206+
`
207+
return this.fetchTemplate()
208+
}
209+
210+
/**
211+
* fetches the template
212+
*/
213+
fetchTemplate () {
214+
switch (this.getAttribute('namespace')) {
215+
case 'dialog-default-':
216+
return this.fetchCSS([{
217+
path: `${this.importMetaUrl}./default-/default-.css`, // apply namespace since it is specific and no fallback
218+
namespace: false
219+
}], false)
220+
case 'dialog-top-slide-in-':
221+
return this.fetchCSS([{
222+
path: `${this.importMetaUrl}./top-slide-in-/top-slide-in-.css`, // apply namespace since it is specific and no fallback
223+
namespace: false
224+
}], false)
225+
case 'dialog-left-slide-in-':
226+
return this.fetchCSS([{
227+
path: `${this.importMetaUrl}./left-slide-in-/left-slide-in-.css`, // apply namespace since it is specific and no fallback
228+
namespace: false
229+
}], false)
230+
case 'dialog-left-slide-in-wide-':
231+
return this.fetchCSS([{
232+
path: `${this.importMetaUrl}./left-slide-in-wide-/left-slide-in-wide-.css`, // apply namespace since it is specific and no fallback
233+
namespace: false
234+
}], false)
235+
case 'dialog-left-slide-in-without-background-':
236+
return this.fetchCSS([{
237+
path: `${this.importMetaUrl}./left-slide-in-without-background-/left-slide-in-without-background-.css`, // apply namespace since it is specific and no fallback
238+
namespace: false
239+
}], false)
240+
case 'dialog-left-slide-in-checkout-':
241+
return this.fetchCSS([{
242+
path: `${this.importMetaUrl}./left-slide-in-checkout-/left-slide-in-checkout-.css`, // apply namespace since it is specific and no fallback
243+
namespace: false
244+
}], false)
245+
}
246+
}
247+
248+
/**
249+
* Render HTML
250+
* @returns Promise<void>
251+
*/
252+
renderHTML () {
253+
this.dialogResolve(this.dialog = this.root.querySelector(this.cssSelector + ' > dialog') || document.createElement('dialog'))
254+
if (this.hasAttribute('autofocus')) this.dialog.setAttribute('autofocus', '')
255+
256+
Array.from(this.root.children).forEach(node => {
257+
if (node === this.dialog || node.getAttribute('slot') || node.nodeName === 'STYLE') return false
258+
if (node.getAttribute('id')?.includes('show') || node.getAttribute('id') === 'open' || node.getAttribute('id') === 'clear') return false
259+
if (node.getAttribute('id') === 'close') return this.dialog.prepend(node)
260+
this.dialog.appendChild(node)
261+
})
262+
263+
this.html = this.dialog
264+
return Promise.resolve()
265+
}
266+
267+
get showNodes () {
268+
return Array.from(this.root.querySelectorAll('[id^=show],[id=open]'))
269+
}
270+
271+
get closeNodes () {
272+
return Array.from(this.root.querySelectorAll('[id^=close]'))
273+
}
274+
275+
setAutofocus () {
276+
const tmpElement = document.createElement('input')
277+
tmpElement.classList.add('hided-input')
278+
this.root.querySelector('dialog').prepend(tmpElement)
279+
tmpElement.focus()
280+
setTimeout(() => {
281+
this.root.querySelector('a-input').root.querySelector('div > input').focus()
282+
tmpElement.remove()
283+
}, 0)
284+
}
285+
}

0 commit comments

Comments
 (0)