@operato/popup 8.0.0-beta.0 → 8.0.0-beta.2
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 +18 -0
- package/package.json +4 -4
- package/.editorconfig +0 -29
- package/.storybook/main.js +0 -3
- package/.storybook/preview.js +0 -52
- package/.storybook/server.mjs +0 -8
- package/demo/index.html +0 -153
- package/demo/ox-popup-list.html +0 -93
- package/demo/ox-popup-menu.html +0 -153
- package/demo/ox-popup.html +0 -42
- package/src/index.ts +0 -7
- package/src/open-popup.ts +0 -166
- package/src/ox-floating-overlay.ts +0 -618
- package/src/ox-popup-list.ts +0 -577
- package/src/ox-popup-menu.ts +0 -247
- package/src/ox-popup-menuitem.ts +0 -187
- package/src/ox-popup.ts +0 -400
- package/src/ox-prompt.ts +0 -549
- package/src/position-converter.ts +0 -37
- package/stories/open-popup.stories.ts +0 -104
- package/stories/ox-popup-list-sortable.stories.ts +0 -215
- package/stories/ox-popup-list.stories.ts +0 -121
- package/stories/ox-popup-menu.stories.ts +0 -188
- package/stories/ox-popup.stories.ts +0 -79
- package/stories/ox-prompt-icon.stories.ts +0 -87
- package/stories/ox-prompt-normal.stories.ts +0 -80
- package/stories/ox-prompt.stories.ts +0 -82
- package/tsconfig.json +0 -25
- package/web-dev-server.config.mjs +0 -27
- package/web-test-runner.config.mjs +0 -41
package/src/ox-popup.ts
DELETED
@@ -1,400 +0,0 @@
|
|
1
|
-
import { css, html, LitElement, nothing } from 'lit'
|
2
|
-
import { render } from 'lit-html'
|
3
|
-
import { customElement, property, state } from 'lit/decorators.js'
|
4
|
-
|
5
|
-
import { ScrollbarStyles } from '@operato/styles'
|
6
|
-
|
7
|
-
import { convertToFixedPosition } from './position-converter.js'
|
8
|
-
|
9
|
-
declare global {
|
10
|
-
interface Window {
|
11
|
-
POPUP_DEBUG?: boolean
|
12
|
-
}
|
13
|
-
}
|
14
|
-
|
15
|
-
/**
|
16
|
-
* Custom element class representing the 'ox-popup' component.
|
17
|
-
*
|
18
|
-
* This component provides the functionality to display a popup with various configuration options.
|
19
|
-
* It can be used to show additional information, notifications, or user interactions within a web application.
|
20
|
-
*
|
21
|
-
* @fires ox-close - Dispatched when the popup is closed.
|
22
|
-
* @fires ox-collapse - Dispatched when the popup is collapsed.
|
23
|
-
*/
|
24
|
-
@customElement('ox-popup')
|
25
|
-
export class OxPopup extends LitElement {
|
26
|
-
static styles = [
|
27
|
-
ScrollbarStyles,
|
28
|
-
css`
|
29
|
-
:host {
|
30
|
-
position: absolute;
|
31
|
-
display: none;
|
32
|
-
z-index: 1000; /* Increased z-index to ensure it's on top */
|
33
|
-
padding: 0;
|
34
|
-
box-shadow: 2px 3px 10px 5px rgba(0, 0, 0, 0.15);
|
35
|
-
box-sizing: border-box;
|
36
|
-
min-width: fit-content;
|
37
|
-
line-height: initial;
|
38
|
-
text-align: initial;
|
39
|
-
background-color: var(--ox-popup-list-background-color, var(--md-sys-color-surface));
|
40
|
-
color: var(--ox-popup-list-color, var(--md-sys-color-on-surface));
|
41
|
-
margin: 0;
|
42
|
-
}
|
43
|
-
|
44
|
-
:host([active]) {
|
45
|
-
display: flex;
|
46
|
-
flex-direction: column;
|
47
|
-
}
|
48
|
-
|
49
|
-
:host(*:focus) {
|
50
|
-
outline: none;
|
51
|
-
}
|
52
|
-
|
53
|
-
#backdrop {
|
54
|
-
position: fixed;
|
55
|
-
top: -100vh;
|
56
|
-
left: -100vw;
|
57
|
-
width: 300vw;
|
58
|
-
height: 300vh;
|
59
|
-
background-color: black;
|
60
|
-
opacity: 0.5;
|
61
|
-
}
|
62
|
-
`
|
63
|
-
]
|
64
|
-
|
65
|
-
@property({ type: Boolean, attribute: 'prevent-close-on-blur' }) preventCloseOnBlur: boolean = false
|
66
|
-
@property({ type: Boolean, attribute: 'backdrop' }) backdrop: boolean = false
|
67
|
-
|
68
|
-
private lastActive: HTMLElement = document.activeElement as HTMLElement
|
69
|
-
protected removeAfterUse: boolean = false
|
70
|
-
|
71
|
-
render() {
|
72
|
-
return html`
|
73
|
-
${this.backdrop ? html`<div id="backdrop" @click=${() => this.close()}></div>` : nothing}
|
74
|
-
<slot></slot>
|
75
|
-
`
|
76
|
-
}
|
77
|
-
|
78
|
-
protected _onfocusout: (e: FocusEvent) => void = function (this: OxPopup, e: FocusEvent) {
|
79
|
-
const to = e.relatedTarget as HTMLElement
|
80
|
-
|
81
|
-
if (!this.contains(to)) {
|
82
|
-
!this.preventCloseOnBlur && !window.POPUP_DEBUG && this.close()
|
83
|
-
}
|
84
|
-
}.bind(this)
|
85
|
-
|
86
|
-
protected _onkeydown: (e: KeyboardEvent) => void = function (this: OxPopup, e: KeyboardEvent) {
|
87
|
-
e.stopPropagation()
|
88
|
-
|
89
|
-
switch (e.key) {
|
90
|
-
case 'Esc': // for IE/Edge
|
91
|
-
case 'Escape':
|
92
|
-
this.close()
|
93
|
-
break
|
94
|
-
}
|
95
|
-
}.bind(this)
|
96
|
-
|
97
|
-
protected _onkeyup: (e: KeyboardEvent) => void = function (this: OxPopup, e: KeyboardEvent) {
|
98
|
-
e.stopPropagation()
|
99
|
-
}.bind(this)
|
100
|
-
|
101
|
-
protected _onmouseup: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {
|
102
|
-
e.stopPropagation()
|
103
|
-
}.bind(this)
|
104
|
-
|
105
|
-
protected _onmousedown: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {
|
106
|
-
e.stopPropagation()
|
107
|
-
}.bind(this)
|
108
|
-
|
109
|
-
protected _oncontextmenu: (e: Event) => void = function (this: OxPopup, e: Event) {
|
110
|
-
e.stopPropagation()
|
111
|
-
// this.close()
|
112
|
-
}.bind(this)
|
113
|
-
|
114
|
-
protected _onclick: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {
|
115
|
-
e.stopPropagation()
|
116
|
-
}.bind(this)
|
117
|
-
|
118
|
-
protected _onclose: (e: Event) => void = function (this: OxPopup, e: Event) {
|
119
|
-
this.close()
|
120
|
-
}.bind(this)
|
121
|
-
|
122
|
-
protected _oncollapse: (e: Event) => void = function (this: OxPopup, e: Event) {
|
123
|
-
e.stopPropagation()
|
124
|
-
this.close()
|
125
|
-
}.bind(this)
|
126
|
-
|
127
|
-
protected _onwindowblur: (e: Event) => void = function (this: OxPopup, e: Event) {
|
128
|
-
!this.preventCloseOnBlur && !window.POPUP_DEBUG && this.close()
|
129
|
-
}.bind(this)
|
130
|
-
|
131
|
-
connectedCallback() {
|
132
|
-
super.connectedCallback()
|
133
|
-
|
134
|
-
this.addEventListener('focusout', this._onfocusout)
|
135
|
-
this.addEventListener('keydown', this._onkeydown)
|
136
|
-
this.addEventListener('keyup', this._onkeyup)
|
137
|
-
this.addEventListener('click', this._onclick)
|
138
|
-
this.addEventListener('mouseup', this._onmouseup)
|
139
|
-
this.addEventListener('mousedown', this._onmousedown)
|
140
|
-
this.addEventListener('contextmenu', this._oncontextmenu)
|
141
|
-
this.addEventListener('ox-close', this._onclose)
|
142
|
-
this.addEventListener('ox-collapse', this._oncollapse)
|
143
|
-
|
144
|
-
this.setAttribute('tabindex', '0') // make this element focusable
|
145
|
-
this.guaranteeFocus()
|
146
|
-
}
|
147
|
-
|
148
|
-
/**
|
149
|
-
* Configuration for opening ox-popup
|
150
|
-
*
|
151
|
-
* @typedef {Object} PopupOpenOptions
|
152
|
-
* @property {HTMLTemplate} template HTMLTemplate to be displayed inside the popup
|
153
|
-
* @property {Number} top The position-top where the pop-up will be displayed
|
154
|
-
* @property {Number} left The position-left where the pop-up will be displayed
|
155
|
-
* @property {Number} right The position-right where the pop-up will be displayed
|
156
|
-
* @property {Number} bottom The position-bottom where the pop-up will be displayed
|
157
|
-
* @property {string} width The maximum width of the popup (CSS string)
|
158
|
-
* @property {string} height The maximum height of the popup (CSS string)
|
159
|
-
* @property {HTMLElement} parent Popup's parent element
|
160
|
-
* @property {string} style Additional CSS styles for the popup
|
161
|
-
* @property {boolean} preventCloseOnBlur Flag to prevent closing the popup on blur
|
162
|
-
* @property {boolean} backdrop If true, a semi-transparent background will be applied behind the popup, dimming the rest of the page.
|
163
|
-
*/
|
164
|
-
static open({
|
165
|
-
template,
|
166
|
-
top,
|
167
|
-
left,
|
168
|
-
right,
|
169
|
-
bottom,
|
170
|
-
width,
|
171
|
-
height,
|
172
|
-
parent,
|
173
|
-
style,
|
174
|
-
preventCloseOnBlur,
|
175
|
-
backdrop
|
176
|
-
}: {
|
177
|
-
template: unknown
|
178
|
-
top?: number
|
179
|
-
left?: number
|
180
|
-
right?: number
|
181
|
-
bottom?: number
|
182
|
-
width?: string
|
183
|
-
height?: string
|
184
|
-
parent?: Element | null
|
185
|
-
style?: string
|
186
|
-
preventCloseOnBlur?: boolean
|
187
|
-
backdrop?: boolean
|
188
|
-
}): OxPopup {
|
189
|
-
const target = document.createElement('ox-popup') as OxPopup
|
190
|
-
if (preventCloseOnBlur) {
|
191
|
-
target.preventCloseOnBlur = preventCloseOnBlur
|
192
|
-
}
|
193
|
-
|
194
|
-
if (backdrop) {
|
195
|
-
target.backdrop = backdrop
|
196
|
-
}
|
197
|
-
|
198
|
-
if (style) {
|
199
|
-
target.style.cssText = style
|
200
|
-
}
|
201
|
-
|
202
|
-
render(template, target)
|
203
|
-
|
204
|
-
if (parent) {
|
205
|
-
var { left, top, right, bottom } = convertToFixedPosition({
|
206
|
-
left,
|
207
|
-
top,
|
208
|
-
right,
|
209
|
-
bottom,
|
210
|
-
relativeElement: parent as HTMLElement
|
211
|
-
})
|
212
|
-
}
|
213
|
-
|
214
|
-
document.body.appendChild(target)
|
215
|
-
target.removeAfterUse = true
|
216
|
-
target.open({ top, left, right, bottom, width, height })
|
217
|
-
|
218
|
-
return target
|
219
|
-
}
|
220
|
-
|
221
|
-
/**
|
222
|
-
* Opens the 'ox-popup' with the specified position and dimensions.
|
223
|
-
*
|
224
|
-
* @param {Object} options - An object specifying the position and dimensions of the popup.
|
225
|
-
* @param {number} options.left - The left position (in pixels) where the popup should be displayed. If not provided, it will be centered horizontally.
|
226
|
-
* @param {number} options.top - The top position (in pixels) where the popup should be displayed. If not provided, it will be centered vertically.
|
227
|
-
* @param {number} options.right - The right position (in pixels) where the popup should be displayed. If provided, it will override the 'left' value.
|
228
|
-
* @param {number} options.bottom - The bottom position (in pixels) where the popup should be displayed. If provided, it will override the 'top' value.
|
229
|
-
* @param {string} options.width - The maximum width of the popup (CSS string). For example, '300px'.
|
230
|
-
* @param {string} options.height - The maximum height of the popup (CSS string). For example, '200px'.
|
231
|
-
* @param {boolean} [options.silent=false] - A flag indicating whether the popup should auto-focus or not. If set to true, the popup won't attempt to focus on any element.
|
232
|
-
* @param {boolean} [options.fixed=false] - A flag indicating whether the popup should be positioned fixed relative to the viewport. If set to true, the popup will be fixed.
|
233
|
-
*/
|
234
|
-
open({
|
235
|
-
left,
|
236
|
-
top,
|
237
|
-
right,
|
238
|
-
bottom,
|
239
|
-
width,
|
240
|
-
height,
|
241
|
-
silent = false,
|
242
|
-
fixed = false
|
243
|
-
}: {
|
244
|
-
left?: number
|
245
|
-
top?: number
|
246
|
-
right?: number
|
247
|
-
bottom?: number
|
248
|
-
width?: string
|
249
|
-
height?: string
|
250
|
-
silent?: boolean
|
251
|
-
fixed?: boolean
|
252
|
-
}) {
|
253
|
-
if (width) {
|
254
|
-
this.style.maxWidth = width
|
255
|
-
this.style.overflowX = 'auto'
|
256
|
-
}
|
257
|
-
|
258
|
-
if (height) {
|
259
|
-
this.style.maxHeight = height
|
260
|
-
this.style.overflowY = 'auto'
|
261
|
-
}
|
262
|
-
|
263
|
-
if (left === undefined && top === undefined && right === undefined && bottom === undefined) {
|
264
|
-
this.style.transform = 'translateX(-50%) translateY(-50%)'
|
265
|
-
this.style.left = '50%'
|
266
|
-
this.style.top = '50%'
|
267
|
-
} else if (fixed) {
|
268
|
-
const bounding = this.parentElement?.getBoundingClientRect() || {
|
269
|
-
left: 0,
|
270
|
-
right: 0,
|
271
|
-
top: 0,
|
272
|
-
bottom: 0
|
273
|
-
}
|
274
|
-
|
275
|
-
this.style.position = 'fixed'
|
276
|
-
|
277
|
-
if (left !== undefined) {
|
278
|
-
left += bounding.left
|
279
|
-
this.style.left = `${left}px`
|
280
|
-
}
|
281
|
-
if (top !== undefined) {
|
282
|
-
top += bounding.top
|
283
|
-
this.style.top = `${top}px`
|
284
|
-
}
|
285
|
-
if (right !== undefined) {
|
286
|
-
right = window.innerWidth - (bounding.right - right)
|
287
|
-
this.style.right = `${right}px`
|
288
|
-
}
|
289
|
-
if (bottom !== undefined) {
|
290
|
-
bottom = window.innerHeight - (bounding.bottom - bottom)
|
291
|
-
this.style.bottom = `${bottom}px`
|
292
|
-
}
|
293
|
-
} else {
|
294
|
-
if (left !== undefined) {
|
295
|
-
this.style.left = `${left}px`
|
296
|
-
}
|
297
|
-
if (top !== undefined) {
|
298
|
-
this.style.top = `${top}px`
|
299
|
-
}
|
300
|
-
if (right !== undefined) {
|
301
|
-
this.style.right = `${right}px`
|
302
|
-
}
|
303
|
-
if (bottom !== undefined) {
|
304
|
-
this.style.bottom = `${bottom}px`
|
305
|
-
}
|
306
|
-
}
|
307
|
-
|
308
|
-
this.setAttribute('active', '')
|
309
|
-
|
310
|
-
requestAnimationFrame(() => {
|
311
|
-
const vh = window.innerHeight
|
312
|
-
const vw = window.innerWidth
|
313
|
-
|
314
|
-
var bounding = this.getBoundingClientRect()
|
315
|
-
|
316
|
-
var h = bounding.height
|
317
|
-
var w = bounding.width
|
318
|
-
var t = bounding.top
|
319
|
-
var l = bounding.left
|
320
|
-
|
321
|
-
// If the popup is too large, it will cause overflow scrolling.
|
322
|
-
if (vh < h) {
|
323
|
-
this.style.height = `${Math.min(Math.max(Math.floor((vh * 2) / 3), vh - (t + 20)), vh)}px`
|
324
|
-
this.style.overflow = 'auto'
|
325
|
-
h = vh
|
326
|
-
}
|
327
|
-
|
328
|
-
if (vw < w) {
|
329
|
-
this.style.width = `${Math.min(Math.max(Math.floor((vw * 2) / 3), vw - (l + 20)), vw)}px`
|
330
|
-
this.style.overflow = 'auto'
|
331
|
-
w = vw
|
332
|
-
}
|
333
|
-
|
334
|
-
// To prevent pop-ups from crossing screen boundaries, use the
|
335
|
-
const computedStyle = getComputedStyle(this)
|
336
|
-
|
337
|
-
if (t < 0) {
|
338
|
-
this.style.top = '10px'
|
339
|
-
this.style.bottom = ''
|
340
|
-
} else if (vh <= t + h) {
|
341
|
-
this.style.top = ''
|
342
|
-
this.style.bottom = '10px'
|
343
|
-
}
|
344
|
-
|
345
|
-
if (l < 0) {
|
346
|
-
this.style.left = `calc(${computedStyle.left} - ${l - 10}px)`
|
347
|
-
this.style.right = ''
|
348
|
-
} else if (vw < l + w) {
|
349
|
-
this.style.left = `calc(${computedStyle.left} - ${l + w - vw + 10}px)`
|
350
|
-
this.style.right = ''
|
351
|
-
}
|
352
|
-
})
|
353
|
-
|
354
|
-
// auto focusing
|
355
|
-
!silent && this.guaranteeFocus()
|
356
|
-
|
357
|
-
/* When the window is out of focus, all pop-ups should disappear. */
|
358
|
-
window.addEventListener('blur', this._onwindowblur)
|
359
|
-
}
|
360
|
-
|
361
|
-
guaranteeFocus(target?: HTMLElement) {
|
362
|
-
const focusible = (target || this).querySelector(
|
363
|
-
':scope > button, :scope > [href], :scope > input, :scope > select, :scope > textarea, :scope > [tabindex]:not([tabindex="-1"])'
|
364
|
-
)
|
365
|
-
|
366
|
-
if (focusible) {
|
367
|
-
;(focusible as HTMLElement).focus()
|
368
|
-
} else {
|
369
|
-
this.focus()
|
370
|
-
}
|
371
|
-
}
|
372
|
-
|
373
|
-
/**
|
374
|
-
* Closes the 'ox-popup'.
|
375
|
-
*/
|
376
|
-
close() {
|
377
|
-
this.removeAttribute('active')
|
378
|
-
|
379
|
-
window.removeEventListener('blur', this._onwindowblur)
|
380
|
-
|
381
|
-
if (this.removeAfterUse && this.parentElement) {
|
382
|
-
/* this case is when the popup is opened by OxPopup.open(...) */
|
383
|
-
this.removeEventListener('focusout', this._onfocusout)
|
384
|
-
this.removeEventListener('keydown', this._onkeydown)
|
385
|
-
this.removeEventListener('keyup', this._onkeyup)
|
386
|
-
this.removeEventListener('click', this._onclick)
|
387
|
-
this.removeEventListener('ox-close', this._onclose)
|
388
|
-
this.removeEventListener('ox-collapse', this._oncollapse)
|
389
|
-
this.removeEventListener('mouseup', this._onmouseup)
|
390
|
-
this.removeEventListener('mousedown', this._onmousedown)
|
391
|
-
this.removeEventListener('contextmenu', this._oncontextmenu)
|
392
|
-
|
393
|
-
this.parentElement.removeChild(this)
|
394
|
-
|
395
|
-
if (this.lastActive) {
|
396
|
-
this.lastActive.focus && this.lastActive.focus()
|
397
|
-
}
|
398
|
-
}
|
399
|
-
}
|
400
|
-
}
|