@operato/popup 0.2.33 → 0.2.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/demo/index.html +2 -2
- package/demo/ox-popup-list.html +2 -2
- package/demo/ox-popup-menu.html +2 -2
- package/demo/ox-popup.html +2 -2
- package/dist/src/ox-popup-list.d.ts +21 -9
- package/dist/src/ox-popup-list.js +108 -52
- package/dist/src/ox-popup-list.js.map +1 -1
- package/dist/src/ox-popup-menu.d.ts +2 -2
- package/dist/src/ox-popup-menu.js +9 -9
- package/dist/src/ox-popup-menu.js.map +1 -1
- package/dist/src/ox-popup-menuitem.d.ts +3 -3
- package/dist/src/ox-popup-menuitem.js +9 -9
- package/dist/src/ox-popup-menuitem.js.map +1 -1
- package/dist/src/ox-popup.d.ts +9 -5
- package/dist/src/ox-popup.js +25 -17
- package/dist/src/ox-popup.js.map +1 -1
- package/dist/test/ox-popup-menu.test.js +2 -2
- package/dist/test/ox-popup-menu.test.js.map +1 -1
- package/dist/test/ox-popup.test.js +1 -1
- package/dist/test/ox-popup.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/ox-popup-list.ts +119 -53
- package/src/ox-popup-menu.ts +8 -8
- package/src/ox-popup-menuitem.ts +9 -9
- package/src/ox-popup.ts +45 -25
- package/test/ox-popup-menu.test.ts +7 -8
- package/test/ox-popup.test.ts +6 -7
package/src/ox-popup-list.ts
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
import { PropertyValues, css, html } from 'lit'
|
|
2
|
-
import { customElement, property } from 'lit/decorators.js'
|
|
2
|
+
import { customElement, property, state } from 'lit/decorators.js'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { OxPopup } from './ox-popup'
|
|
5
5
|
import { render } from 'lit-html'
|
|
6
6
|
|
|
7
|
-
function
|
|
8
|
-
|
|
7
|
+
function guaranteeFocus(element: HTMLElement) {
|
|
8
|
+
// 1. 옵션 엘리먼트의 하위 첫번째 focusible 엘리먼트에 focus 기회를 준다.
|
|
9
|
+
const focusible = element.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
|
|
10
|
+
|
|
11
|
+
if (focusible) {
|
|
12
|
+
;(focusible as HTMLElement).focus()
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 2. 자신을 포함해서 가장 가까운 부모에게 focus 기회를 준다.
|
|
9
17
|
const closest = element.closest(
|
|
10
18
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
11
19
|
) as HTMLElement
|
|
12
20
|
|
|
13
21
|
closest?.focus()
|
|
14
|
-
|
|
15
|
-
return closest
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
@customElement('ox-popup-list')
|
|
19
|
-
export class
|
|
25
|
+
export class OxPopupList extends OxPopup {
|
|
20
26
|
static styles = [
|
|
21
|
-
...
|
|
27
|
+
...OxPopup.styles,
|
|
22
28
|
css`
|
|
23
29
|
:host {
|
|
24
30
|
display: none;
|
|
@@ -41,6 +47,10 @@ export class PopupList extends Popup {
|
|
|
41
47
|
outline: none;
|
|
42
48
|
}
|
|
43
49
|
|
|
50
|
+
::slotted([option]) {
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
::slotted(*) {
|
|
45
55
|
margin: 1px 0;
|
|
46
56
|
padding: 4px 2px 3px 2px;
|
|
@@ -59,6 +69,11 @@ export class PopupList extends Popup {
|
|
|
59
69
|
cursor: pointer;
|
|
60
70
|
}
|
|
61
71
|
|
|
72
|
+
::slotted([option][selected]) {
|
|
73
|
+
background-color: red;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
}
|
|
76
|
+
|
|
62
77
|
::slotted([separator]) {
|
|
63
78
|
height: 1px;
|
|
64
79
|
width: 100%;
|
|
@@ -71,13 +86,23 @@ export class PopupList extends Popup {
|
|
|
71
86
|
`
|
|
72
87
|
]
|
|
73
88
|
|
|
74
|
-
@property({ type:
|
|
89
|
+
@property({ type: Boolean }) multiple: boolean = false
|
|
90
|
+
@property({ type: String, attribute: 'attr-selected' }) attrSelected?: string
|
|
91
|
+
|
|
92
|
+
@state() activeIndex?: number
|
|
75
93
|
|
|
76
94
|
render() {
|
|
77
|
-
return html`
|
|
95
|
+
return html`
|
|
96
|
+
<slot
|
|
97
|
+
@change=${(e: Event) => {
|
|
98
|
+
e.stopPropagation()
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
</slot>
|
|
102
|
+
`
|
|
78
103
|
}
|
|
79
104
|
|
|
80
|
-
protected _onkeydown: (e: KeyboardEvent) => void = function (this:
|
|
105
|
+
protected _onkeydown: (e: KeyboardEvent) => void = function (this: OxPopupList, e: KeyboardEvent) {
|
|
81
106
|
e.stopPropagation()
|
|
82
107
|
|
|
83
108
|
switch (e.key) {
|
|
@@ -90,88 +115,125 @@ export class PopupList extends Popup {
|
|
|
90
115
|
case 'ArrowLeft':
|
|
91
116
|
case 'Up': // for IE/Edge
|
|
92
117
|
case 'ArrowUp':
|
|
93
|
-
this.activeIndex
|
|
118
|
+
this.activeIndex!--
|
|
94
119
|
break
|
|
95
120
|
|
|
96
121
|
case 'Right': // for IE/Edge
|
|
97
122
|
case 'ArrowRight':
|
|
98
123
|
case 'Down': // for IE/Edge
|
|
99
124
|
case 'ArrowDown':
|
|
100
|
-
this.activeIndex
|
|
125
|
+
this.activeIndex!++
|
|
101
126
|
break
|
|
102
127
|
|
|
103
128
|
case 'Enter':
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
129
|
+
case ' ':
|
|
130
|
+
case 'Spacebar': // for old firefox
|
|
131
|
+
this.setActive(this.activeIndex!, true)
|
|
132
|
+
this.select()
|
|
109
133
|
break
|
|
110
134
|
}
|
|
111
135
|
}.bind(this)
|
|
112
136
|
|
|
113
|
-
protected _onfocusout: (e: FocusEvent) => void = function (this:
|
|
114
|
-
const target = e.target as HTMLElement
|
|
137
|
+
protected _onfocusout: (e: FocusEvent) => void = function (this: OxPopupList, e: FocusEvent) {
|
|
115
138
|
const to = e.relatedTarget as HTMLElement
|
|
116
|
-
const from = target.closest('ox-popup-list')
|
|
117
139
|
|
|
118
|
-
if (!to
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.setActive(this.activeIndex)
|
|
123
|
-
} else {
|
|
124
|
-
if (!this.contains(to)) {
|
|
125
|
-
/* 분명히 내 범위가 아닌 엘리먼트로 포커스가 옮겨졌다면, ox-popup-list는 닫혀야 한다. */
|
|
126
|
-
// @ts-ignore for debug
|
|
127
|
-
!window.POPUP_DEBUG && this.close()
|
|
128
|
-
}
|
|
140
|
+
if (!this.contains(to)) {
|
|
141
|
+
/* 분명히 내 범위가 아닌 엘리먼트로 포커스가 옮겨졌다면, ox-popup-list는 닫혀야 한다. */
|
|
142
|
+
// @ts-ignore for debug
|
|
143
|
+
!window.POPUP_DEBUG && this.close()
|
|
129
144
|
}
|
|
130
145
|
}.bind(this)
|
|
131
146
|
|
|
132
|
-
protected _onclick: (e: MouseEvent) => void = function (this:
|
|
147
|
+
protected _onclick: (e: MouseEvent) => void = function (this: OxPopupList, e: MouseEvent) {
|
|
133
148
|
e.stopPropagation()
|
|
134
149
|
|
|
135
150
|
const option = (e.target as HTMLElement)?.closest('[option]')
|
|
136
151
|
if (option) {
|
|
137
|
-
this.setActive(option)
|
|
138
|
-
this.select(
|
|
152
|
+
this.setActive(option, true)
|
|
153
|
+
this.select()
|
|
139
154
|
}
|
|
140
155
|
}.bind(this)
|
|
141
156
|
|
|
142
157
|
updated(changes: PropertyValues<this>) {
|
|
143
158
|
if (changes.has('activeIndex')) {
|
|
144
|
-
this.setActive(this.activeIndex)
|
|
159
|
+
this.activeIndex !== undefined && this.setActive(this.activeIndex)
|
|
145
160
|
}
|
|
146
161
|
}
|
|
147
162
|
|
|
148
|
-
select(
|
|
149
|
-
|
|
163
|
+
async select() {
|
|
164
|
+
await this.updateComplete
|
|
165
|
+
|
|
166
|
+
const options = Array.from(this.querySelectorAll(':scope > [option]'))
|
|
150
167
|
|
|
151
|
-
|
|
168
|
+
const selected = options
|
|
169
|
+
.filter(option => option.hasAttribute('value') && option.hasAttribute(this.attrSelected || 'selected'))
|
|
170
|
+
.map(option => option.getAttribute('value'))
|
|
171
|
+
|
|
172
|
+
this.dispatchEvent(
|
|
173
|
+
new CustomEvent('select', {
|
|
174
|
+
bubbles: true,
|
|
175
|
+
composed: true,
|
|
176
|
+
detail: this.multiple ? selected : selected[0]
|
|
177
|
+
})
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
const option = options[this.activeIndex!]
|
|
181
|
+
if (!option.hasAttribute('alive-on-select') && !this.hasAttribute('multiple')) {
|
|
152
182
|
this.close()
|
|
153
183
|
}
|
|
154
184
|
}
|
|
155
185
|
|
|
156
|
-
setActive(active: number | Element | null) {
|
|
157
|
-
const options = Array.from(this.querySelectorAll('
|
|
186
|
+
setActive(active: number | Element | null, withSelect?: boolean) {
|
|
187
|
+
const options = Array.from(this.querySelectorAll('[option]'))
|
|
188
|
+
|
|
189
|
+
if (active instanceof Element) {
|
|
190
|
+
const index = options.findIndex(option => option === active)
|
|
191
|
+
this.setActive(index === -1 ? 0 : index, withSelect)
|
|
192
|
+
return
|
|
193
|
+
}
|
|
158
194
|
|
|
159
|
-
options.
|
|
195
|
+
options.forEach(async (option, index) => {
|
|
160
196
|
if (typeof active === 'number' && index === (active + options.length) % options.length) {
|
|
161
197
|
option.setAttribute('active', '')
|
|
162
|
-
|
|
198
|
+
if (withSelect && !this.attrSelected) {
|
|
199
|
+
this.multiple ? option.toggleAttribute('selected') : option.setAttribute('selected', '')
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
guaranteeFocus(option as HTMLElement)
|
|
163
203
|
|
|
164
|
-
this.activeIndex = index
|
|
165
|
-
} else if (active === option) {
|
|
166
204
|
this.activeIndex = index
|
|
167
205
|
} else {
|
|
168
206
|
option.removeAttribute('active')
|
|
207
|
+
!this.attrSelected && !this.multiple && withSelect && option.removeAttribute('selected')
|
|
169
208
|
}
|
|
170
209
|
})
|
|
171
210
|
}
|
|
172
211
|
|
|
212
|
+
override open(params: { left?: number; top?: number; right?: number; bottom?: number; silent?: boolean }) {
|
|
213
|
+
super.open(params)
|
|
214
|
+
|
|
215
|
+
if (this.activeIndex === undefined) {
|
|
216
|
+
this.activeIndex = 0
|
|
217
|
+
} else {
|
|
218
|
+
this.setActive(this.activeIndex)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
override close() {
|
|
223
|
+
if (this.hasAttribute('active')) {
|
|
224
|
+
this.dispatchEvent(
|
|
225
|
+
new CustomEvent('close', {
|
|
226
|
+
bubbles: true,
|
|
227
|
+
composed: true
|
|
228
|
+
})
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
super.close()
|
|
233
|
+
}
|
|
234
|
+
|
|
173
235
|
/**
|
|
174
|
-
* Open
|
|
236
|
+
* Open OxPopup
|
|
175
237
|
*
|
|
176
238
|
* @param {PopupOpenOptions}
|
|
177
239
|
*/
|
|
@@ -179,21 +241,25 @@ export class PopupList extends Popup {
|
|
|
179
241
|
template,
|
|
180
242
|
top,
|
|
181
243
|
left,
|
|
244
|
+
right,
|
|
245
|
+
bottom,
|
|
182
246
|
parent
|
|
183
247
|
}: {
|
|
184
248
|
template: unknown
|
|
185
|
-
top
|
|
186
|
-
left
|
|
249
|
+
top?: number
|
|
250
|
+
left?: number
|
|
251
|
+
right?: number
|
|
252
|
+
bottom?: number
|
|
187
253
|
parent?: Element | null
|
|
188
254
|
}) {
|
|
189
255
|
const owner = parent || document.body
|
|
190
|
-
const target = document.createElement('ox-popup-list') as
|
|
256
|
+
const target = document.createElement('ox-popup-list') as OxPopupList
|
|
191
257
|
render(template, target)
|
|
192
258
|
|
|
193
|
-
target.style.left = `${left}px`
|
|
194
|
-
target.style.top = `${top}px`
|
|
195
|
-
|
|
196
|
-
target.
|
|
259
|
+
if (left !== undefined) target.style.left = `${left}px`
|
|
260
|
+
if (top !== undefined) target.style.top = `${top}px`
|
|
261
|
+
if (right !== undefined) target.style.right = `${right}px`
|
|
262
|
+
if (bottom !== undefined) target.style.bottom = `${bottom}px`
|
|
197
263
|
|
|
198
264
|
target._parent = owner
|
|
199
265
|
|
package/src/ox-popup-menu.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PropertyValues, css, html } from 'lit'
|
|
2
2
|
import { customElement, property } from 'lit/decorators.js'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { OxPopup } from './ox-popup'
|
|
5
5
|
import { render } from 'lit-html'
|
|
6
6
|
|
|
7
7
|
function focusClosest(element: HTMLElement) {
|
|
@@ -16,9 +16,9 @@ function focusClosest(element: HTMLElement) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
@customElement('ox-popup-menu')
|
|
19
|
-
export class
|
|
19
|
+
export class OxPopupMenu extends OxPopup {
|
|
20
20
|
static styles = [
|
|
21
|
-
...
|
|
21
|
+
...OxPopup.styles,
|
|
22
22
|
css`
|
|
23
23
|
:host {
|
|
24
24
|
display: none;
|
|
@@ -79,7 +79,7 @@ export class PopupMenu extends Popup {
|
|
|
79
79
|
return html` <slot> </slot> `
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
protected _onkeydown: (e: KeyboardEvent) => void = function (this:
|
|
82
|
+
protected _onkeydown: (e: KeyboardEvent) => void = function (this: OxPopupMenu, e: KeyboardEvent) {
|
|
83
83
|
e.stopPropagation()
|
|
84
84
|
|
|
85
85
|
switch (e.key) {
|
|
@@ -112,7 +112,7 @@ export class PopupMenu extends Popup {
|
|
|
112
112
|
}
|
|
113
113
|
}.bind(this)
|
|
114
114
|
|
|
115
|
-
protected _onfocusout: (e: FocusEvent) => void = function (this:
|
|
115
|
+
protected _onfocusout: (e: FocusEvent) => void = function (this: OxPopupMenu, e: FocusEvent) {
|
|
116
116
|
const target = e.target as HTMLElement
|
|
117
117
|
const to = e.relatedTarget as HTMLElement
|
|
118
118
|
const from = target.closest('ox-popup-menu')
|
|
@@ -131,7 +131,7 @@ export class PopupMenu extends Popup {
|
|
|
131
131
|
}
|
|
132
132
|
}.bind(this)
|
|
133
133
|
|
|
134
|
-
protected _onclick: (e: MouseEvent) => void = function (this:
|
|
134
|
+
protected _onclick: (e: MouseEvent) => void = function (this: OxPopupMenu, e: MouseEvent) {
|
|
135
135
|
e.stopPropagation()
|
|
136
136
|
|
|
137
137
|
const menu = (e.target as HTMLElement)?.closest('[menu], ox-popup-menuitem')
|
|
@@ -148,7 +148,7 @@ export class PopupMenu extends Popup {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
select(menu: Element) {
|
|
151
|
-
menu.dispatchEvent(new CustomEvent('
|
|
151
|
+
menu.dispatchEvent(new CustomEvent('select'))
|
|
152
152
|
if (!menu.hasAttribute('alive-on-select')) {
|
|
153
153
|
this.dispatchEvent(new CustomEvent('ox-close', { bubbles: true, composed: true, detail: this }))
|
|
154
154
|
}
|
|
@@ -195,7 +195,7 @@ export class PopupMenu extends Popup {
|
|
|
195
195
|
parent?: Element | null
|
|
196
196
|
}) {
|
|
197
197
|
const owner = parent || document.body
|
|
198
|
-
const target = document.createElement('ox-popup-menu') as
|
|
198
|
+
const target = document.createElement('ox-popup-menu') as OxPopupMenu
|
|
199
199
|
render(template, target)
|
|
200
200
|
|
|
201
201
|
target.style.left = `${left}px`
|
package/src/ox-popup-menuitem.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { LitElement, PropertyValues, css, html } from 'lit'
|
|
2
2
|
import { customElement, property, state } from 'lit/decorators.js'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { OxPopupMenu } from './ox-popup-menu'
|
|
5
5
|
|
|
6
6
|
@customElement('ox-popup-menuitem')
|
|
7
|
-
export class
|
|
7
|
+
export class OxPopupMenuItem extends LitElement {
|
|
8
8
|
static styles = [
|
|
9
9
|
css`
|
|
10
10
|
:host {
|
|
@@ -52,7 +52,7 @@ export class PopupMenuItem extends LitElement {
|
|
|
52
52
|
@property({ type: Boolean }) active: boolean = false
|
|
53
53
|
@property({ type: String }) label!: string
|
|
54
54
|
|
|
55
|
-
@state() _submenu?:
|
|
55
|
+
@state() _submenu?: OxPopupMenu
|
|
56
56
|
|
|
57
57
|
render() {
|
|
58
58
|
return html`
|
|
@@ -73,26 +73,26 @@ export class PopupMenuItem extends LitElement {
|
|
|
73
73
|
this.addEventListener('click', this._onclick)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
protected _onclick: (e: MouseEvent) => void = function (this:
|
|
76
|
+
protected _onclick: (e: MouseEvent) => void = function (this: OxPopupMenuItem, e: MouseEvent) {
|
|
77
77
|
if (!this._submenu) {
|
|
78
78
|
return
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
e.stopPropagation()
|
|
82
82
|
|
|
83
|
-
const parent = this.closest('ox-popup-menu') as
|
|
83
|
+
const parent = this.closest('ox-popup-menu') as OxPopupMenu
|
|
84
84
|
if (parent) {
|
|
85
85
|
parent.setActive(this)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
this.dispatchEvent(new CustomEvent('
|
|
88
|
+
this.dispatchEvent(new CustomEvent('select'))
|
|
89
89
|
|
|
90
90
|
requestAnimationFrame(() => {
|
|
91
91
|
this.expand(false)
|
|
92
92
|
})
|
|
93
93
|
}.bind(this)
|
|
94
94
|
|
|
95
|
-
protected _onkeydown: (e: KeyboardEvent) => void = function (this:
|
|
95
|
+
protected _onkeydown: (e: KeyboardEvent) => void = function (this: OxPopupMenuItem, e: KeyboardEvent) {
|
|
96
96
|
switch (e.key) {
|
|
97
97
|
case 'Right':
|
|
98
98
|
case 'ArrowRight':
|
|
@@ -115,8 +115,8 @@ export class PopupMenuItem extends LitElement {
|
|
|
115
115
|
}
|
|
116
116
|
}.bind(this)
|
|
117
117
|
|
|
118
|
-
protected _onslotchange: (e: Event) => void = function (this:
|
|
119
|
-
this._submenu = this.querySelector('ox-popup-menu') as
|
|
118
|
+
protected _onslotchange: (e: Event) => void = function (this: OxPopupMenuItem, e: Event) {
|
|
119
|
+
this._submenu = this.querySelector('ox-popup-menu') as OxPopupMenu
|
|
120
120
|
}.bind(this)
|
|
121
121
|
|
|
122
122
|
updated(changes: PropertyValues<this>) {
|
package/src/ox-popup.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { LitElement, css, html } from 'lit'
|
|
2
2
|
import { customElement, state } from 'lit/decorators.js'
|
|
3
3
|
|
|
4
|
+
import { ScrollbarStyles } from '@operato/styles'
|
|
4
5
|
import { render } from 'lit-html'
|
|
5
6
|
|
|
6
7
|
@customElement('ox-popup')
|
|
7
|
-
export class
|
|
8
|
+
export class OxPopup extends LitElement {
|
|
8
9
|
static styles = [
|
|
10
|
+
ScrollbarStyles,
|
|
9
11
|
css`
|
|
10
12
|
:host {
|
|
11
13
|
position: absolute;
|
|
12
14
|
display: none;
|
|
13
15
|
background-color: white;
|
|
14
16
|
z-index: 100;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
min-width: fit-content;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
:host([active]) {
|
|
@@ -30,7 +34,7 @@ export class Popup extends LitElement {
|
|
|
30
34
|
return html` <slot> </slot> `
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
protected _onfocusout: (e: FocusEvent) => void = function (this:
|
|
37
|
+
protected _onfocusout: (e: FocusEvent) => void = function (this: OxPopup, e: FocusEvent) {
|
|
34
38
|
const to = e.relatedTarget as HTMLElement
|
|
35
39
|
|
|
36
40
|
if (!this.contains(to)) {
|
|
@@ -40,7 +44,7 @@ export class Popup extends LitElement {
|
|
|
40
44
|
}
|
|
41
45
|
}.bind(this)
|
|
42
46
|
|
|
43
|
-
protected _onkeydown: (e: KeyboardEvent) => void = function (this:
|
|
47
|
+
protected _onkeydown: (e: KeyboardEvent) => void = function (this: OxPopup, e: KeyboardEvent) {
|
|
44
48
|
e.stopPropagation()
|
|
45
49
|
|
|
46
50
|
switch (e.key) {
|
|
@@ -51,24 +55,24 @@ export class Popup extends LitElement {
|
|
|
51
55
|
}
|
|
52
56
|
}.bind(this)
|
|
53
57
|
|
|
54
|
-
protected _onkeyup: (e: KeyboardEvent) => void = function (this:
|
|
58
|
+
protected _onkeyup: (e: KeyboardEvent) => void = function (this: OxPopup, e: KeyboardEvent) {
|
|
55
59
|
e.stopPropagation()
|
|
56
60
|
}.bind(this)
|
|
57
61
|
|
|
58
|
-
protected _onclick: (e: MouseEvent) => void = function (this:
|
|
62
|
+
protected _onclick: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {
|
|
59
63
|
e.stopPropagation()
|
|
60
64
|
}.bind(this)
|
|
61
65
|
|
|
62
|
-
protected _onclose: (e: Event) => void = function (this:
|
|
66
|
+
protected _onclose: (e: Event) => void = function (this: OxPopup, e: Event) {
|
|
63
67
|
this.close()
|
|
64
68
|
}.bind(this)
|
|
65
69
|
|
|
66
|
-
protected _oncollapse: (e: Event) => void = function (this:
|
|
70
|
+
protected _oncollapse: (e: Event) => void = function (this: OxPopup, e: Event) {
|
|
67
71
|
e.stopPropagation()
|
|
68
72
|
this.close()
|
|
69
73
|
}.bind(this)
|
|
70
74
|
|
|
71
|
-
protected _onwindowblur: (e: Event) => void = function (this:
|
|
75
|
+
protected _onwindowblur: (e: Event) => void = function (this: OxPopup, e: Event) {
|
|
72
76
|
// @ts-ignore for debug
|
|
73
77
|
!window.POPUP_DEBUG && this.close()
|
|
74
78
|
}.bind(this)
|
|
@@ -83,9 +87,6 @@ export class Popup extends LitElement {
|
|
|
83
87
|
this.addEventListener('ox-close', this._onclose)
|
|
84
88
|
this.addEventListener('ox-collapse', this._oncollapse)
|
|
85
89
|
|
|
86
|
-
/* When the window is out of focus, all pop-ups should disappear. */
|
|
87
|
-
window.addEventListener('blur', this._onwindowblur)
|
|
88
|
-
|
|
89
90
|
this.setAttribute('tabindex', '0') // make this element focusable
|
|
90
91
|
this.guaranteeFocus()
|
|
91
92
|
}
|
|
@@ -109,33 +110,52 @@ export class Popup extends LitElement {
|
|
|
109
110
|
template,
|
|
110
111
|
top,
|
|
111
112
|
left,
|
|
113
|
+
right,
|
|
114
|
+
bottom,
|
|
112
115
|
parent
|
|
113
116
|
}: {
|
|
114
117
|
template: unknown
|
|
115
|
-
top
|
|
116
|
-
left
|
|
118
|
+
top?: number
|
|
119
|
+
left?: number
|
|
120
|
+
right?: number
|
|
121
|
+
bottom?: number
|
|
117
122
|
parent?: Element | null
|
|
118
123
|
}) {
|
|
119
124
|
const owner = parent || document.body
|
|
120
|
-
const target = document.createElement('ox-popup') as
|
|
121
|
-
render(template, target)
|
|
125
|
+
const target = document.createElement('ox-popup') as OxPopup
|
|
122
126
|
|
|
123
|
-
target
|
|
124
|
-
target.style.top = `${top}px`
|
|
125
|
-
|
|
126
|
-
target.setAttribute('active', '')
|
|
127
|
+
render(template, target)
|
|
127
128
|
|
|
128
129
|
target._parent = owner
|
|
129
130
|
owner.appendChild(target)
|
|
131
|
+
|
|
132
|
+
target.open({ top, left, right, bottom })
|
|
130
133
|
}
|
|
131
134
|
|
|
132
|
-
open({
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
open({
|
|
136
|
+
left,
|
|
137
|
+
top,
|
|
138
|
+
right,
|
|
139
|
+
bottom,
|
|
140
|
+
silent = false
|
|
141
|
+
}: {
|
|
142
|
+
left?: number
|
|
143
|
+
top?: number
|
|
144
|
+
right?: number
|
|
145
|
+
bottom?: number
|
|
146
|
+
silent?: boolean
|
|
147
|
+
}) {
|
|
148
|
+
if (left !== undefined) this.style.left = `${left}px`
|
|
149
|
+
if (top !== undefined) this.style.top = `${top}px`
|
|
150
|
+
if (right !== undefined) this.style.right = `${right}px`
|
|
151
|
+
if (bottom !== undefined) this.style.bottom = `${bottom}px`
|
|
135
152
|
|
|
136
153
|
this.setAttribute('active', '')
|
|
137
154
|
|
|
138
155
|
!silent && this.guaranteeFocus()
|
|
156
|
+
|
|
157
|
+
/* When the window is out of focus, all pop-ups should disappear. */
|
|
158
|
+
window.addEventListener('blur', this._onwindowblur)
|
|
139
159
|
}
|
|
140
160
|
|
|
141
161
|
guaranteeFocus() {
|
|
@@ -153,8 +173,10 @@ export class Popup extends LitElement {
|
|
|
153
173
|
close() {
|
|
154
174
|
this.removeAttribute('active')
|
|
155
175
|
|
|
176
|
+
window.removeEventListener('blur', this._onwindowblur)
|
|
177
|
+
|
|
156
178
|
if (this._parent) {
|
|
157
|
-
/* this case is when the popup is opened by
|
|
179
|
+
/* this case is when the popup is opened by OxPopup.open(...) */
|
|
158
180
|
this.removeEventListener('focusout', this._onfocusout)
|
|
159
181
|
this.removeEventListener('keydown', this._onkeydown)
|
|
160
182
|
this.removeEventListener('keyup', this._onkeyup)
|
|
@@ -162,8 +184,6 @@ export class Popup extends LitElement {
|
|
|
162
184
|
this.removeEventListener('ox-close', this._onclose)
|
|
163
185
|
this.removeEventListener('ox-collapse', this._oncollapse)
|
|
164
186
|
|
|
165
|
-
window.removeEventListener('blur', this._onwindowblur)
|
|
166
|
-
|
|
167
187
|
this._parent.removeChild(this)
|
|
168
188
|
delete this._parent
|
|
169
189
|
}
|
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
import '../src/ox-popup-menu'
|
|
2
2
|
|
|
3
|
-
import { html } from 'lit'
|
|
4
|
-
|
|
5
3
|
import { expect, fixture } from '@open-wc/testing'
|
|
6
4
|
|
|
7
|
-
import {
|
|
5
|
+
import { OxPopupMenu } from '../src/ox-popup-menu'
|
|
6
|
+
import { html } from 'lit'
|
|
8
7
|
|
|
9
|
-
describe('
|
|
8
|
+
describe('OxPopupMenu', () => {
|
|
10
9
|
it('has a default title "Hey there" and counter 5', async () => {
|
|
11
|
-
const el = await fixture<
|
|
10
|
+
const el = await fixture<OxPopupMenu>(html`<ox-popup-menu></ox-popup-menu>`)
|
|
12
11
|
|
|
13
12
|
expect(el.title).to.equal('Hey there')
|
|
14
13
|
// expect(el.counter).to.equal(5)
|
|
15
14
|
})
|
|
16
15
|
|
|
17
16
|
it('increases the counter on button click', async () => {
|
|
18
|
-
const el = await fixture<
|
|
17
|
+
const el = await fixture<OxPopupMenu>(html`<ox-popup-menu></ox-popup-menu>`)
|
|
19
18
|
el.shadowRoot!.querySelector('button')!.click()
|
|
20
19
|
|
|
21
20
|
// expect(el.counter).to.equal(6)
|
|
22
21
|
})
|
|
23
22
|
|
|
24
23
|
it('can override the title via attribute', async () => {
|
|
25
|
-
const el = await fixture<
|
|
24
|
+
const el = await fixture<OxPopupMenu>(html`<ox-popup-menu title="attribute title"></ox-popup-menu>`)
|
|
26
25
|
|
|
27
26
|
expect(el.title).to.equal('attribute title')
|
|
28
27
|
})
|
|
29
28
|
|
|
30
29
|
it('passes the a11y audit', async () => {
|
|
31
|
-
const el = await fixture<
|
|
30
|
+
const el = await fixture<OxPopupMenu>(html`<ox-popup-menu></ox-popup-menu>`)
|
|
32
31
|
|
|
33
32
|
await expect(el).shadowDom.to.be.accessible()
|
|
34
33
|
})
|
package/test/ox-popup.test.ts
CHANGED
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
import '../src/ox-popup'
|
|
2
2
|
|
|
3
|
-
import { html } from 'lit'
|
|
4
|
-
|
|
5
3
|
import { expect, fixture } from '@open-wc/testing'
|
|
6
4
|
|
|
7
|
-
import {
|
|
5
|
+
import { OxPopup } from '../src/ox-popup'
|
|
6
|
+
import { html } from 'lit'
|
|
8
7
|
|
|
9
8
|
describe('Popup', () => {
|
|
10
9
|
it('has a default title "Hey there" and counter 5', async () => {
|
|
11
|
-
const el = await fixture<
|
|
10
|
+
const el = await fixture<OxPopup>(html`<ox-popup></ox-popup>`)
|
|
12
11
|
|
|
13
12
|
expect(el.title).to.equal('Hey there')
|
|
14
13
|
// expect(el.counter).to.equal(5)
|
|
15
14
|
})
|
|
16
15
|
|
|
17
16
|
it('increases the counter on button click', async () => {
|
|
18
|
-
const el = await fixture<
|
|
17
|
+
const el = await fixture<OxPopup>(html`<ox-popup></ox-popup>`)
|
|
19
18
|
el.shadowRoot!.querySelector('button')!.click()
|
|
20
19
|
|
|
21
20
|
// expect(el.counter).to.equal(6)
|
|
22
21
|
})
|
|
23
22
|
|
|
24
23
|
it('can override the title via attribute', async () => {
|
|
25
|
-
const el = await fixture<
|
|
24
|
+
const el = await fixture<OxPopup>(html`<ox-popup title="attribute title"></ox-popup>`)
|
|
26
25
|
|
|
27
26
|
expect(el.title).to.equal('attribute title')
|
|
28
27
|
})
|
|
29
28
|
|
|
30
29
|
it('passes the a11y audit', async () => {
|
|
31
|
-
const el = await fixture<
|
|
30
|
+
const el = await fixture<OxPopup>(html`<ox-popup></ox-popup>`)
|
|
32
31
|
|
|
33
32
|
await expect(el).shadowDom.to.be.accessible()
|
|
34
33
|
})
|