@operato/popup 10.29.49 → 10.29.59
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 +24 -0
- package/dist/src/ox-floating-overlay.js +1 -1
- package/dist/src/ox-floating-overlay.js.map +1 -1
- package/dist/src/ox-popup-list.d.ts +2 -0
- package/dist/src/ox-popup-list.js +34 -1
- package/dist/src/ox-popup-list.js.map +1 -1
- package/dist/src/ox-popup.d.ts +16 -1
- package/dist/src/ox-popup.js +47 -3
- package/dist/src/ox-popup.js.map +1 -1
- package/dist/test/ox-popup-asked-size.test.d.ts +1 -0
- package/dist/test/ox-popup-asked-size.test.js +43 -0
- package/dist/test/ox-popup-asked-size.test.js.map +1 -0
- package/dist/test/ox-popup-list-search.test.d.ts +1 -0
- package/dist/test/ox-popup-list-search.test.js +61 -0
- package/dist/test/ox-popup-list-search.test.js.map +1 -0
- package/dist/test/ox-popup-placement.test.d.ts +1 -0
- package/dist/test/ox-popup-placement.test.js +69 -0
- package/dist/test/ox-popup-placement.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/test/ox-popup-asked-size.test.ts +48 -0
- package/test/ox-popup-list-search.test.ts +71 -0
- package/test/ox-popup-placement.test.ts +81 -0
package/dist/src/ox-popup.js
CHANGED
|
@@ -20,6 +20,8 @@ let OxPopup = class OxPopup extends LitElement {
|
|
|
20
20
|
this.backdrop = false;
|
|
21
21
|
this.lastActive = document.activeElement;
|
|
22
22
|
this.removeAfterUse = false;
|
|
23
|
+
/** The height its opener gave it in `style` (OxPopup.open): where every opening starts, before any cut. */
|
|
24
|
+
this.askedHeight = { height: '', maxHeight: '' };
|
|
23
25
|
this._onfocusout = function (e) {
|
|
24
26
|
const to = e.relatedTarget;
|
|
25
27
|
if (!this.contains(to)) {
|
|
@@ -145,6 +147,7 @@ let OxPopup = class OxPopup extends LitElement {
|
|
|
145
147
|
}
|
|
146
148
|
if (style) {
|
|
147
149
|
target.style.cssText = style;
|
|
150
|
+
target.askedHeight = { height: target.style.height, maxHeight: target.style.maxHeight };
|
|
148
151
|
}
|
|
149
152
|
render(template, target);
|
|
150
153
|
if (parent) {
|
|
@@ -173,8 +176,10 @@ let OxPopup = class OxPopup extends LitElement {
|
|
|
173
176
|
* @param {string} options.height - The maximum height of the popup (CSS string). For example, '200px'.
|
|
174
177
|
* @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.
|
|
175
178
|
* @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.
|
|
179
|
+
* @param {number} [options.anchorTop] - Where the element the popup opens under begins, in the same terms as `top` (which is where it ends).
|
|
180
|
+
* A popup with too little room below opens above that line instead. Without it, `top` is a point.
|
|
176
181
|
*/
|
|
177
|
-
open({ left, top, right, bottom, width, height, silent = false, fixed = false }) {
|
|
182
|
+
open({ left, top, right, bottom, width, height, silent = false, fixed = false, anchorTop }) {
|
|
178
183
|
if (this.backdrop) {
|
|
179
184
|
this.createBackdrop();
|
|
180
185
|
}
|
|
@@ -182,6 +187,13 @@ let OxPopup = class OxPopup extends LitElement {
|
|
|
182
187
|
this.style.maxWidth = width;
|
|
183
188
|
this.style.overflowX = 'auto';
|
|
184
189
|
}
|
|
190
|
+
/* A popup opened again starts from where it is asked to open, not from where it was last moved or cut. */
|
|
191
|
+
this.style.height = this.askedHeight.height;
|
|
192
|
+
this.style.maxHeight = this.askedHeight.maxHeight;
|
|
193
|
+
if (top !== undefined && bottom === undefined)
|
|
194
|
+
this.style.bottom = '';
|
|
195
|
+
if (bottom !== undefined && top === undefined)
|
|
196
|
+
this.style.top = '';
|
|
185
197
|
if (height) {
|
|
186
198
|
this.style.maxHeight = height;
|
|
187
199
|
this.style.overflowY = 'auto';
|
|
@@ -240,7 +252,7 @@ let OxPopup = class OxPopup extends LitElement {
|
|
|
240
252
|
var t = bounding.top;
|
|
241
253
|
var l = bounding.left;
|
|
242
254
|
// If the popup is too large, it will cause overflow scrolling.
|
|
243
|
-
if (vh < h) {
|
|
255
|
+
if (vh < h && (top === undefined || bottom !== undefined)) {
|
|
244
256
|
this.style.height = `${Math.min(Math.max(Math.floor((vh * 2) / 3), vh - (t + 20)), vh)}px`;
|
|
245
257
|
this.style.overflow = 'auto';
|
|
246
258
|
h = vh;
|
|
@@ -252,7 +264,10 @@ let OxPopup = class OxPopup extends LitElement {
|
|
|
252
264
|
}
|
|
253
265
|
// To prevent pop-ups from crossing screen boundaries, use the
|
|
254
266
|
const computedStyle = getComputedStyle(this);
|
|
255
|
-
if (
|
|
267
|
+
if (top !== undefined && bottom === undefined) {
|
|
268
|
+
this.keepOffTheLine(bounding, top, anchorTop, computedStyle.top);
|
|
269
|
+
}
|
|
270
|
+
else if (t < 0) {
|
|
256
271
|
this.style.top = '10px';
|
|
257
272
|
this.style.bottom = '';
|
|
258
273
|
}
|
|
@@ -274,6 +289,35 @@ let OxPopup = class OxPopup extends LitElement {
|
|
|
274
289
|
/* When the window is out of focus, all pop-ups should disappear. */
|
|
275
290
|
window.addEventListener('blur', this._onwindowblur);
|
|
276
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* A popup opened downward from a line - the bottom of its select, or a point - never covers that line or what is
|
|
294
|
+
* above it down to `anchorTop`: covered, the select's next press lands on the popup (a double-click in a grid cell
|
|
295
|
+
* picked an option). With too little room below it opens above, if there is more room there; either way it is cut
|
|
296
|
+
* to the room it has and scrolls inside.
|
|
297
|
+
*/
|
|
298
|
+
keepOffTheLine(bounding, top, anchorTop, styledTop) {
|
|
299
|
+
const margin = 10;
|
|
300
|
+
const height = bounding.height;
|
|
301
|
+
const line = bounding.top;
|
|
302
|
+
const lineAbove = anchorTop === undefined ? line : line - (top - anchorTop);
|
|
303
|
+
const below = window.innerHeight - line - margin;
|
|
304
|
+
const above = lineAbove - margin;
|
|
305
|
+
if (height <= below) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (above > below) {
|
|
309
|
+
const fitted = Math.min(height, above);
|
|
310
|
+
if (fitted < height) {
|
|
311
|
+
this.style.maxHeight = `${fitted}px`;
|
|
312
|
+
this.style.overflowY = 'auto';
|
|
313
|
+
}
|
|
314
|
+
this.style.top = `calc(${styledTop} - ${line - (lineAbove - fitted)}px)`;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
this.style.maxHeight = `${Math.max(below, 0)}px`;
|
|
318
|
+
this.style.overflowY = 'auto';
|
|
319
|
+
}
|
|
320
|
+
}
|
|
277
321
|
guaranteeFocus(target) {
|
|
278
322
|
const focusible = (target || this).querySelector(':host > button, :host > [href], :host > input, :host > select, :host > textarea, :host > [tabindex]:not([tabindex="-1"])');
|
|
279
323
|
if (focusible) {
|
package/dist/src/ox-popup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ox-popup.js","sourceRoot":"","sources":["../../src/ox-popup.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing } from 'lit'\nimport { render } from 'lit-html'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { convertToFixedPosition } from './position-converter.js'\n\ndeclare global {\n interface Window {\n POPUP_DEBUG?: boolean\n }\n}\n\n/**\n * Custom element class representing the 'ox-popup' component.\n *\n * This component provides the functionality to display a popup with various configuration options.\n * It can be used to show additional information, notifications, or user interactions within a web application.\n *\n * @fires ox-close - Dispatched when the popup is closed.\n * @fires ox-collapse - Dispatched when the popup is collapsed.\n */\n@customElement('ox-popup')\nexport class OxPopup extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n position: absolute;\n display: none;\n z-index: 1000; /* Increased z-index to ensure it's on top */\n padding: 0;\n box-shadow: 2px 3px 10px 5px rgba(0, 0, 0, 0.15);\n box-sizing: border-box;\n min-width: fit-content;\n line-height: initial;\n text-align: initial;\n background-color: var(--ox-popup-list-background-color, var(--md-sys-color-surface));\n color: var(--ox-popup-list-color, var(--md-sys-color-on-surface));\n margin: 0;\n }\n\n :host([active]) {\n display: flex;\n flex-direction: column;\n }\n\n :host(*:focus) {\n outline: none;\n }\n\n #backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n background-color: black;\n opacity: 0.5;\n }\n `\n ]\n\n @property({ type: Boolean, attribute: 'prevent-close-on-blur' }) preventCloseOnBlur: boolean = false\n @property({ type: Boolean, attribute: 'backdrop' }) backdrop: boolean = false\n\n private lastActive: HTMLElement = document.activeElement as HTMLElement\n protected removeAfterUse: boolean = false\n\n render() {\n return html` <slot></slot> `\n }\n\n protected _onfocusout: (e: FocusEvent) => void = function (this: OxPopup, e: FocusEvent) {\n const to = e.relatedTarget as HTMLElement\n\n if (!this.contains(to)) {\n !this.preventCloseOnBlur && !window.POPUP_DEBUG && this.close()\n }\n }.bind(this)\n\n protected _onkeydown: (e: KeyboardEvent) => void = function (this: OxPopup, e: KeyboardEvent) {\n e.stopPropagation()\n\n switch (e.key) {\n case 'Esc': // for IE/Edge\n case 'Escape':\n this.close()\n break\n }\n }.bind(this)\n\n protected _onkeyup: (e: KeyboardEvent) => void = function (this: OxPopup, e: KeyboardEvent) {\n e.stopPropagation()\n }.bind(this)\n\n protected _onmouseup: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {\n e.stopPropagation()\n }.bind(this)\n\n protected _onmousedown: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {\n e.stopPropagation()\n }.bind(this)\n\n protected _oncontextmenu: (e: Event) => void = function (this: OxPopup, e: Event) {\n e.stopPropagation()\n // this.close()\n }.bind(this)\n\n protected _onclick: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {\n e.stopPropagation()\n }.bind(this)\n\n protected _onclose: (e: Event) => void = function (this: OxPopup, e: Event) {\n this.close()\n }.bind(this)\n\n protected _oncollapse: (e: Event) => void = function (this: OxPopup, e: Event) {\n e.stopPropagation()\n this.close()\n }.bind(this)\n\n protected _onwindowblur: (e: Event) => void = function (this: OxPopup, e: Event) {\n !this.preventCloseOnBlur && !window.POPUP_DEBUG && this.close()\n }.bind(this)\n\n connectedCallback() {\n super.connectedCallback()\n\n this.addEventListener('focusout', this._onfocusout)\n this.addEventListener('keydown', this._onkeydown)\n this.addEventListener('keyup', this._onkeyup)\n this.addEventListener('click', this._onclick)\n this.addEventListener('mouseup', this._onmouseup)\n this.addEventListener('mousedown', this._onmousedown)\n this.addEventListener('contextmenu', this._oncontextmenu)\n this.addEventListener('ox-close', this._onclose)\n this.addEventListener('ox-collapse', this._oncollapse)\n\n this.setAttribute('tabindex', '0') // make this element focusable\n requestAnimationFrame(() => {\n this.guaranteeFocus()\n })\n }\n\n /**\n * Configuration for opening ox-popup\n *\n * @typedef {Object} PopupOpenOptions\n * @property {HTMLTemplate} template HTMLTemplate to be displayed inside the popup\n * @property {Number} top The position-top where the pop-up will be displayed\n * @property {Number} left The position-left where the pop-up will be displayed\n * @property {Number} right The position-right where the pop-up will be displayed\n * @property {Number} bottom The position-bottom where the pop-up will be displayed\n * @property {string} width The maximum width of the popup (CSS string)\n * @property {string} height The maximum height of the popup (CSS string)\n * @property {HTMLElement} parent Popup's parent element\n * @property {string} style Additional CSS styles for the popup\n * @property {boolean} preventCloseOnBlur Flag to prevent closing the popup on blur\n * @property {boolean} backdrop If true, a semi-transparent background will be applied behind the popup, dimming the rest of the page.\n */\n static open({\n template,\n top,\n left,\n right,\n bottom,\n width,\n height,\n parent,\n style,\n preventCloseOnBlur,\n backdrop\n }: {\n template: unknown\n top?: number\n left?: number\n right?: number\n bottom?: number\n width?: string\n height?: string\n parent?: Element | null\n style?: string\n preventCloseOnBlur?: boolean\n backdrop?: boolean\n }): OxPopup {\n const target = document.createElement('ox-popup') as OxPopup\n if (preventCloseOnBlur) {\n target.preventCloseOnBlur = preventCloseOnBlur\n }\n\n if (backdrop) {\n target.backdrop = backdrop\n }\n\n if (style) {\n target.style.cssText = style\n }\n\n render(template, target)\n\n if (parent) {\n var { left, top, right, bottom } = convertToFixedPosition({\n left,\n top,\n right,\n bottom,\n relativeElement: parent as HTMLElement\n })\n }\n\n document.body.appendChild(target)\n target.removeAfterUse = true\n target.open({ top, left, right, bottom, width, height })\n\n return target\n }\n\n /**\n * Opens the 'ox-popup' with the specified position and dimensions.\n *\n * @param {Object} options - An object specifying the position and dimensions of the popup.\n * @param {number} options.left - The left position (in pixels) where the popup should be displayed. If not provided, it will be centered horizontally.\n * @param {number} options.top - The top position (in pixels) where the popup should be displayed. If not provided, it will be centered vertically.\n * @param {number} options.right - The right position (in pixels) where the popup should be displayed. If provided, it will override the 'left' value.\n * @param {number} options.bottom - The bottom position (in pixels) where the popup should be displayed. If provided, it will override the 'top' value.\n * @param {string} options.width - The maximum width of the popup (CSS string). For example, '300px'.\n * @param {string} options.height - The maximum height of the popup (CSS string). For example, '200px'.\n * @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.\n * @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.\n */\n open({\n left,\n top,\n right,\n bottom,\n width,\n height,\n silent = false,\n fixed = false\n }: {\n left?: number\n top?: number\n right?: number\n bottom?: number\n width?: string\n height?: string\n silent?: boolean\n fixed?: boolean\n }) {\n if (this.backdrop) {\n this.createBackdrop()\n }\n\n if (width) {\n this.style.maxWidth = width\n this.style.overflowX = 'auto'\n }\n\n if (height) {\n this.style.maxHeight = height\n this.style.overflowY = 'auto'\n }\n\n if (left === undefined && top === undefined && right === undefined && bottom === undefined) {\n this.style.transform = 'translateX(-50%) translateY(-50%)'\n this.style.left = '50%'\n this.style.top = '50%'\n } else if (fixed) {\n const bounding = this.parentElement?.getBoundingClientRect() || {\n left: 0,\n right: 0,\n top: 0,\n bottom: 0\n }\n\n this.style.position = 'fixed'\n\n if (left !== undefined) {\n left += bounding.left\n this.style.left = `${left}px`\n }\n if (top !== undefined) {\n top += bounding.top\n this.style.top = `${top}px`\n }\n if (right !== undefined) {\n right = window.innerWidth - (bounding.right - right)\n this.style.right = `${right}px`\n }\n if (bottom !== undefined) {\n bottom = window.innerHeight - (bounding.bottom - bottom)\n this.style.bottom = `${bottom}px`\n }\n } else {\n if (left !== undefined) {\n this.style.left = `${left}px`\n }\n if (top !== undefined) {\n this.style.top = `${top}px`\n }\n if (right !== undefined) {\n this.style.right = `${right}px`\n }\n if (bottom !== undefined) {\n this.style.bottom = `${bottom}px`\n }\n }\n\n this.setAttribute('active', '')\n\n requestAnimationFrame(() => {\n const vh = window.innerHeight\n const vw = window.innerWidth\n\n var bounding = this.getBoundingClientRect()\n\n var h = bounding.height\n var w = bounding.width\n var t = bounding.top\n var l = bounding.left\n\n // If the popup is too large, it will cause overflow scrolling.\n if (vh < h) {\n this.style.height = `${Math.min(Math.max(Math.floor((vh * 2) / 3), vh - (t + 20)), vh)}px`\n this.style.overflow = 'auto'\n h = vh\n }\n\n if (vw < w) {\n this.style.width = `${Math.min(Math.max(Math.floor((vw * 2) / 3), vw - (l + 20)), vw)}px`\n this.style.overflow = 'auto'\n w = vw\n }\n\n // To prevent pop-ups from crossing screen boundaries, use the\n const computedStyle = getComputedStyle(this)\n\n if (t < 0) {\n this.style.top = '10px'\n this.style.bottom = ''\n } else if (vh <= t + h) {\n this.style.top = ''\n this.style.bottom = '10px'\n }\n\n if (l < 0) {\n this.style.left = `calc(${computedStyle.left} - ${l - 10}px)`\n this.style.right = ''\n } else if (vw < l + w) {\n this.style.left = `calc(${computedStyle.left} - ${l + w - vw + 10}px)`\n this.style.right = ''\n }\n })\n\n // auto focusing\n !silent && this.guaranteeFocus()\n\n /* When the window is out of focus, all pop-ups should disappear. */\n window.addEventListener('blur', this._onwindowblur)\n }\n\n guaranteeFocus(target?: HTMLElement) {\n const focusible = (target || this).querySelector(\n ':host > button, :host > [href], :host > input, :host > select, :host > textarea, :host > [tabindex]:not([tabindex=\"-1\"])'\n )\n\n if (focusible) {\n ;(focusible as HTMLElement).focus()\n } else {\n this.focus()\n }\n }\n\n /**\n * Closes the 'ox-popup'.\n */\n close() {\n this.removeAttribute('active')\n\n window.removeEventListener('blur', this._onwindowblur)\n\n if (this.backdrop) {\n this.removeBackdrop()\n }\n\n if (this.removeAfterUse && this.parentElement) {\n /* this case is when the popup is opened by OxPopup.open(...) */\n this.removeEventListener('focusout', this._onfocusout)\n this.removeEventListener('keydown', this._onkeydown)\n this.removeEventListener('keyup', this._onkeyup)\n this.removeEventListener('click', this._onclick)\n this.removeEventListener('ox-close', this._onclose)\n this.removeEventListener('ox-collapse', this._oncollapse)\n this.removeEventListener('mouseup', this._onmouseup)\n this.removeEventListener('mousedown', this._onmousedown)\n this.removeEventListener('contextmenu', this._oncontextmenu)\n\n this.parentElement.removeChild(this)\n\n if (this.lastActive) {\n this.lastActive.focus && this.lastActive.focus()\n }\n }\n }\n\n private createBackdrop() {\n let backdrop = document.createElement('div')\n backdrop.id = 'ox-popup-backdrop'\n backdrop.style.position = 'fixed'\n backdrop.style.top = '0'\n backdrop.style.left = '0'\n backdrop.style.width = '100vw'\n backdrop.style.height = '100vh'\n backdrop.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'\n backdrop.style.zIndex = '999'\n backdrop.style.backdropFilter = 'blur(5px)'\n backdrop.style.transition = 'opacity 0.3s ease-in-out'\n backdrop.style.opacity = '0'\n\n document.body.appendChild(backdrop)\n requestAnimationFrame(() => {\n backdrop.style.opacity = '1'\n })\n\n backdrop.addEventListener('click', () => this.close())\n }\n\n private removeBackdrop() {\n const backdrops = document.querySelectorAll('#ox-popup-backdrop')\n\n if (backdrops.length > 0) {\n const lastBackdrop = backdrops[backdrops.length - 1] as HTMLElement\n lastBackdrop.style.opacity = '0'\n setTimeout(() => {\n lastBackdrop.remove()\n }, 300)\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ox-popup.js","sourceRoot":"","sources":["../../src/ox-popup.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAW,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAS,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAQhE;;;;;;;;GAQG;AAEI,IAAM,OAAO,GAAb,MAAM,OAAQ,SAAQ,UAAU;IAAhC;;QAwC4D,uBAAkB,GAAY,KAAK,CAAA;QAChD,aAAQ,GAAY,KAAK,CAAA;QAErE,eAAU,GAAgB,QAAQ,CAAC,aAA4B,CAAA;QAC7D,mBAAc,GAAY,KAAK,CAAA;QACzC,2GAA2G;QACjG,gBAAW,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;QAM3C,gBAAW,GAA4B,UAAyB,CAAa;YACrF,MAAM,EAAE,GAAG,CAAC,CAAC,aAA4B,CAAA;YAEzC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBACvB,CAAC,IAAI,CAAC,kBAAkB,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC,KAAK,EAAE,CAAA;YACjE,CAAC;QACH,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEF,eAAU,GAA+B,UAAyB,CAAgB;YAC1F,CAAC,CAAC,eAAe,EAAE,CAAA;YAEnB,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC;gBACd,KAAK,KAAK,CAAC,CAAC,cAAc;gBAC1B,KAAK,QAAQ;oBACX,IAAI,CAAC,KAAK,EAAE,CAAA;oBACZ,MAAK;YACT,CAAC;QACH,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEF,aAAQ,GAA+B,UAAyB,CAAgB;YACxF,CAAC,CAAC,eAAe,EAAE,CAAA;QACrB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEF,eAAU,GAA4B,UAAyB,CAAa;YACpF,CAAC,CAAC,eAAe,EAAE,CAAA;QACrB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEF,iBAAY,GAA4B,UAAyB,CAAa;YACtF,CAAC,CAAC,eAAe,EAAE,CAAA;QACrB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEF,mBAAc,GAAuB,UAAyB,CAAQ;YAC9E,CAAC,CAAC,eAAe,EAAE,CAAA;YACnB,eAAe;QACjB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEF,aAAQ,GAA4B,UAAyB,CAAa;YAClF,CAAC,CAAC,eAAe,EAAE,CAAA;QACrB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEF,aAAQ,GAAuB,UAAyB,CAAQ;YACxE,IAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEF,gBAAW,GAAuB,UAAyB,CAAQ;YAC3E,CAAC,CAAC,eAAe,EAAE,CAAA;YACnB,IAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEF,kBAAa,GAAuB,UAAyB,CAAQ;YAC7E,CAAC,IAAI,CAAC,kBAAkB,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC,KAAK,EAAE,CAAA;QACjE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAuWd,CAAC;aA7cQ,WAAM,GAAG;QACd,eAAe;QACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkCF;KACF,AArCY,CAqCZ;IAUD,MAAM;QACJ,OAAO,IAAI,CAAA,iBAAiB,CAAA;IAC9B,CAAC;IAuDD,iBAAiB;QACf,KAAK,CAAC,iBAAiB,EAAE,CAAA;QAEzB,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;QACnD,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;QACjD,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC7C,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC7C,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;QACjD,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;QACrD,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;QACzD,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAChD,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;QAEtD,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA,CAAC,8BAA8B;QACjE,qBAAqB,CAAC,GAAG,EAAE;YACzB,IAAI,CAAC,cAAc,EAAE,CAAA;QACvB,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,IAAI,CAAC,EACV,QAAQ,EACR,GAAG,EACH,IAAI,EACJ,KAAK,EACL,MAAM,EACN,KAAK,EACL,MAAM,EACN,MAAM,EACN,KAAK,EACL,kBAAkB,EAClB,QAAQ,EAaT;QACC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAY,CAAA;QAC5D,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,CAAC,kBAAkB,GAAG,kBAAkB,CAAA;QAChD,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAA;QAC5B,CAAC;QAED,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAA;YAC5B,MAAM,CAAC,WAAW,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAA;QACzF,CAAC;QAED,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAExB,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,sBAAsB,CAAC;gBACxD,IAAI;gBACJ,GAAG;gBACH,KAAK;gBACL,MAAM;gBACN,eAAe,EAAE,MAAqB;aACvC,CAAC,CAAA;QACJ,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QACjC,MAAM,CAAC,cAAc,GAAG,IAAI,CAAA;QAC5B,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;QAExD,OAAO,MAAM,CAAA;IACf,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,IAAI,CAAC,EACH,IAAI,EACJ,GAAG,EACH,KAAK,EACL,MAAM,EACN,KAAK,EACL,MAAM,EACN,MAAM,GAAG,KAAK,EACd,KAAK,GAAG,KAAK,EACb,SAAS,EAWV;QACC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,cAAc,EAAE,CAAA;QACvB,CAAC;QAED,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAA;YAC3B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,MAAM,CAAA;QAC/B,CAAC;QAED,0GAA0G;QAC1G,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAA;QAC3C,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAA;QACjD,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAA;QACrE,IAAI,MAAM,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAA;QAElE,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,MAAM,CAAA;YAC7B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,MAAM,CAAA;QAC/B,CAAC;QAED,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC3F,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,mCAAmC,CAAA;YAC1D,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAA;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAA;QACxB,CAAC;aAAM,IAAI,KAAK,EAAE,CAAC;YACjB,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,qBAAqB,EAAE,IAAI;gBAC9D,IAAI,EAAE,CAAC;gBACP,KAAK,EAAE,CAAC;gBACR,GAAG,EAAE,CAAC;gBACN,MAAM,EAAE,CAAC;aACV,CAAA;YAED,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAA;YAE7B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAA;gBACrB,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,IAAI,IAAI,CAAA;YAC/B,CAAC;YACD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAA;gBACnB,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,GAAG,IAAI,CAAA;YAC7B,CAAC;YACD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,KAAK,GAAG,MAAM,CAAC,UAAU,GAAG,CAAC,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC,CAAA;gBACpD,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,KAAK,IAAI,CAAA;YACjC,CAAC;YACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM,GAAG,MAAM,CAAC,WAAW,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,CAAA;gBACxD,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAA;YACnC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,IAAI,IAAI,CAAA;YAC/B,CAAC;YACD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,GAAG,IAAI,CAAA;YAC7B,CAAC;YACD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,KAAK,IAAI,CAAA;YACjC,CAAC;YACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAA;YACnC,CAAC;QACH,CAAC;QAED,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;QAE/B,qBAAqB,CAAC,GAAG,EAAE;YACzB,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAA;YAC7B,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAA;YAE5B,IAAI,QAAQ,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAA;YAE3C,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAA;YACvB,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAA;YACtB,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAA;YACpB,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAA;YAErB,+DAA+D;YAC/D,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC,EAAE,CAAC;gBAC1D,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAA;gBAC1F,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAA;gBAC5B,CAAC,GAAG,EAAE,CAAA;YACR,CAAC;YAED,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAA;gBACzF,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAA;gBAC5B,CAAC,GAAG,EAAE,CAAA;YACR,CAAC;YAED,8DAA8D;YAC9D,MAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;YAE5C,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC9C,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,CAAA;YAClE,CAAC;iBAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjB,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAA;gBACvB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAA;YACxB,CAAC;iBAAM,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAA;gBACnB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;YAC5B,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACV,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,QAAQ,aAAa,CAAC,IAAI,MAAM,CAAC,GAAG,EAAE,KAAK,CAAA;gBAC7D,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAA;YACvB,CAAC;iBAAM,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,QAAQ,aAAa,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAA;gBACtE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAA;YACvB,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,gBAAgB;QAChB,CAAC,MAAM,IAAI,IAAI,CAAC,cAAc,EAAE,CAAA;QAEhC,oEAAoE;QACpE,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;IACrD,CAAC;IAED;;;;;OAKG;IACK,cAAc,CAAC,QAAiB,EAAE,GAAW,EAAE,SAA6B,EAAE,SAAiB;QACrG,MAAM,MAAM,GAAG,EAAE,CAAA;QACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAA;QAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAA;QACzB,MAAM,SAAS,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,SAAS,CAAC,CAAA;QAC3E,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,GAAG,IAAI,GAAG,MAAM,CAAA;QAChD,MAAM,KAAK,GAAG,SAAS,GAAG,MAAM,CAAA;QAEhC,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;YACpB,OAAM;QACR,CAAC;QAED,IAAI,KAAK,GAAG,KAAK,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;YACtC,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC;gBACpB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,MAAM,IAAI,CAAA;gBACpC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,MAAM,CAAA;YAC/B,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,QAAQ,SAAS,MAAM,IAAI,GAAG,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,CAAA;QAC1E,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAA;YAChD,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,MAAM,CAAA;QAC/B,CAAC;IACH,CAAC;IAED,cAAc,CAAC,MAAoB;QACjC,MAAM,SAAS,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,aAAa,CAC9C,0HAA0H,CAC3H,CAAA;QAED,IAAI,SAAS,EAAE,CAAC;YACd,CAAC;YAAC,SAAyB,CAAC,KAAK,EAAE,CAAA;QACrC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAA;QAE9B,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;QAEtD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,cAAc,EAAE,CAAA;QACvB,CAAC;QAED,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YAC9C,gEAAgE;YAChE,IAAI,CAAC,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;YACtD,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;YACpD,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;YAChD,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;YAChD,IAAI,CAAC,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;YACnD,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;YACzD,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;YACpD,IAAI,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;YACxD,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;YAE5D,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;YAEpC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACpB,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAA;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IAEO,cAAc;QACpB,IAAI,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;QAC5C,QAAQ,CAAC,EAAE,GAAG,mBAAmB,CAAA;QACjC,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAA;QACjC,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAA;QACxB,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAA;QACzB,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAA;QAC9B,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,OAAO,CAAA;QAC/B,QAAQ,CAAC,KAAK,CAAC,eAAe,GAAG,oBAAoB,CAAA;QACrD,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAA;QAC7B,QAAQ,CAAC,KAAK,CAAC,cAAc,GAAG,WAAW,CAAA;QAC3C,QAAQ,CAAC,KAAK,CAAC,UAAU,GAAG,0BAA0B,CAAA;QACtD,QAAQ,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAA;QAE5B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAA;QACnC,qBAAqB,CAAC,GAAG,EAAE;YACzB,QAAQ,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAA;QAC9B,CAAC,CAAC,CAAA;QAEF,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;IACxD,CAAC;IAEO,cAAc;QACpB,MAAM,SAAS,GAAG,QAAQ,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAA;QAEjE,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,YAAY,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAgB,CAAA;YACnE,YAAY,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAA;YAChC,UAAU,CAAC,GAAG,EAAE;gBACd,YAAY,CAAC,MAAM,EAAE,CAAA;YACvB,CAAC,EAAE,GAAG,CAAC,CAAA;QACT,CAAC;IACH,CAAC;;AAragE;IAAhE,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,CAAC;mDAAoC;AAChD;IAAnD,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;yCAA0B;AAzClE,OAAO;IADnB,aAAa,CAAC,UAAU,CAAC;GACb,OAAO,CA8cnB","sourcesContent":["import { css, html, LitElement, nothing } from 'lit'\nimport { render } from 'lit-html'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { convertToFixedPosition } from './position-converter.js'\n\ndeclare global {\n interface Window {\n POPUP_DEBUG?: boolean\n }\n}\n\n/**\n * Custom element class representing the 'ox-popup' component.\n *\n * This component provides the functionality to display a popup with various configuration options.\n * It can be used to show additional information, notifications, or user interactions within a web application.\n *\n * @fires ox-close - Dispatched when the popup is closed.\n * @fires ox-collapse - Dispatched when the popup is collapsed.\n */\n@customElement('ox-popup')\nexport class OxPopup extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n position: absolute;\n display: none;\n z-index: 1000; /* Increased z-index to ensure it's on top */\n padding: 0;\n box-shadow: 2px 3px 10px 5px rgba(0, 0, 0, 0.15);\n box-sizing: border-box;\n min-width: fit-content;\n line-height: initial;\n text-align: initial;\n background-color: var(--ox-popup-list-background-color, var(--md-sys-color-surface));\n color: var(--ox-popup-list-color, var(--md-sys-color-on-surface));\n margin: 0;\n }\n\n :host([active]) {\n display: flex;\n flex-direction: column;\n }\n\n :host(*:focus) {\n outline: none;\n }\n\n #backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n background-color: black;\n opacity: 0.5;\n }\n `\n ]\n\n @property({ type: Boolean, attribute: 'prevent-close-on-blur' }) preventCloseOnBlur: boolean = false\n @property({ type: Boolean, attribute: 'backdrop' }) backdrop: boolean = false\n\n private lastActive: HTMLElement = document.activeElement as HTMLElement\n protected removeAfterUse: boolean = false\n /** The height its opener gave it in `style` (OxPopup.open): where every opening starts, before any cut. */\n protected askedHeight = { height: '', maxHeight: '' }\n\n render() {\n return html` <slot></slot> `\n }\n\n protected _onfocusout: (e: FocusEvent) => void = function (this: OxPopup, e: FocusEvent) {\n const to = e.relatedTarget as HTMLElement\n\n if (!this.contains(to)) {\n !this.preventCloseOnBlur && !window.POPUP_DEBUG && this.close()\n }\n }.bind(this)\n\n protected _onkeydown: (e: KeyboardEvent) => void = function (this: OxPopup, e: KeyboardEvent) {\n e.stopPropagation()\n\n switch (e.key) {\n case 'Esc': // for IE/Edge\n case 'Escape':\n this.close()\n break\n }\n }.bind(this)\n\n protected _onkeyup: (e: KeyboardEvent) => void = function (this: OxPopup, e: KeyboardEvent) {\n e.stopPropagation()\n }.bind(this)\n\n protected _onmouseup: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {\n e.stopPropagation()\n }.bind(this)\n\n protected _onmousedown: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {\n e.stopPropagation()\n }.bind(this)\n\n protected _oncontextmenu: (e: Event) => void = function (this: OxPopup, e: Event) {\n e.stopPropagation()\n // this.close()\n }.bind(this)\n\n protected _onclick: (e: MouseEvent) => void = function (this: OxPopup, e: MouseEvent) {\n e.stopPropagation()\n }.bind(this)\n\n protected _onclose: (e: Event) => void = function (this: OxPopup, e: Event) {\n this.close()\n }.bind(this)\n\n protected _oncollapse: (e: Event) => void = function (this: OxPopup, e: Event) {\n e.stopPropagation()\n this.close()\n }.bind(this)\n\n protected _onwindowblur: (e: Event) => void = function (this: OxPopup, e: Event) {\n !this.preventCloseOnBlur && !window.POPUP_DEBUG && this.close()\n }.bind(this)\n\n connectedCallback() {\n super.connectedCallback()\n\n this.addEventListener('focusout', this._onfocusout)\n this.addEventListener('keydown', this._onkeydown)\n this.addEventListener('keyup', this._onkeyup)\n this.addEventListener('click', this._onclick)\n this.addEventListener('mouseup', this._onmouseup)\n this.addEventListener('mousedown', this._onmousedown)\n this.addEventListener('contextmenu', this._oncontextmenu)\n this.addEventListener('ox-close', this._onclose)\n this.addEventListener('ox-collapse', this._oncollapse)\n\n this.setAttribute('tabindex', '0') // make this element focusable\n requestAnimationFrame(() => {\n this.guaranteeFocus()\n })\n }\n\n /**\n * Configuration for opening ox-popup\n *\n * @typedef {Object} PopupOpenOptions\n * @property {HTMLTemplate} template HTMLTemplate to be displayed inside the popup\n * @property {Number} top The position-top where the pop-up will be displayed\n * @property {Number} left The position-left where the pop-up will be displayed\n * @property {Number} right The position-right where the pop-up will be displayed\n * @property {Number} bottom The position-bottom where the pop-up will be displayed\n * @property {string} width The maximum width of the popup (CSS string)\n * @property {string} height The maximum height of the popup (CSS string)\n * @property {HTMLElement} parent Popup's parent element\n * @property {string} style Additional CSS styles for the popup\n * @property {boolean} preventCloseOnBlur Flag to prevent closing the popup on blur\n * @property {boolean} backdrop If true, a semi-transparent background will be applied behind the popup, dimming the rest of the page.\n */\n static open({\n template,\n top,\n left,\n right,\n bottom,\n width,\n height,\n parent,\n style,\n preventCloseOnBlur,\n backdrop\n }: {\n template: unknown\n top?: number\n left?: number\n right?: number\n bottom?: number\n width?: string\n height?: string\n parent?: Element | null\n style?: string\n preventCloseOnBlur?: boolean\n backdrop?: boolean\n }): OxPopup {\n const target = document.createElement('ox-popup') as OxPopup\n if (preventCloseOnBlur) {\n target.preventCloseOnBlur = preventCloseOnBlur\n }\n\n if (backdrop) {\n target.backdrop = backdrop\n }\n\n if (style) {\n target.style.cssText = style\n target.askedHeight = { height: target.style.height, maxHeight: target.style.maxHeight }\n }\n\n render(template, target)\n\n if (parent) {\n var { left, top, right, bottom } = convertToFixedPosition({\n left,\n top,\n right,\n bottom,\n relativeElement: parent as HTMLElement\n })\n }\n\n document.body.appendChild(target)\n target.removeAfterUse = true\n target.open({ top, left, right, bottom, width, height })\n\n return target\n }\n\n /**\n * Opens the 'ox-popup' with the specified position and dimensions.\n *\n * @param {Object} options - An object specifying the position and dimensions of the popup.\n * @param {number} options.left - The left position (in pixels) where the popup should be displayed. If not provided, it will be centered horizontally.\n * @param {number} options.top - The top position (in pixels) where the popup should be displayed. If not provided, it will be centered vertically.\n * @param {number} options.right - The right position (in pixels) where the popup should be displayed. If provided, it will override the 'left' value.\n * @param {number} options.bottom - The bottom position (in pixels) where the popup should be displayed. If provided, it will override the 'top' value.\n * @param {string} options.width - The maximum width of the popup (CSS string). For example, '300px'.\n * @param {string} options.height - The maximum height of the popup (CSS string). For example, '200px'.\n * @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.\n * @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.\n * @param {number} [options.anchorTop] - Where the element the popup opens under begins, in the same terms as `top` (which is where it ends).\n * A popup with too little room below opens above that line instead. Without it, `top` is a point.\n */\n open({\n left,\n top,\n right,\n bottom,\n width,\n height,\n silent = false,\n fixed = false,\n anchorTop\n }: {\n left?: number\n top?: number\n right?: number\n bottom?: number\n width?: string\n height?: string\n silent?: boolean\n fixed?: boolean\n anchorTop?: number\n }) {\n if (this.backdrop) {\n this.createBackdrop()\n }\n\n if (width) {\n this.style.maxWidth = width\n this.style.overflowX = 'auto'\n }\n\n /* A popup opened again starts from where it is asked to open, not from where it was last moved or cut. */\n this.style.height = this.askedHeight.height\n this.style.maxHeight = this.askedHeight.maxHeight\n if (top !== undefined && bottom === undefined) this.style.bottom = ''\n if (bottom !== undefined && top === undefined) this.style.top = ''\n\n if (height) {\n this.style.maxHeight = height\n this.style.overflowY = 'auto'\n }\n\n if (left === undefined && top === undefined && right === undefined && bottom === undefined) {\n this.style.transform = 'translateX(-50%) translateY(-50%)'\n this.style.left = '50%'\n this.style.top = '50%'\n } else if (fixed) {\n const bounding = this.parentElement?.getBoundingClientRect() || {\n left: 0,\n right: 0,\n top: 0,\n bottom: 0\n }\n\n this.style.position = 'fixed'\n\n if (left !== undefined) {\n left += bounding.left\n this.style.left = `${left}px`\n }\n if (top !== undefined) {\n top += bounding.top\n this.style.top = `${top}px`\n }\n if (right !== undefined) {\n right = window.innerWidth - (bounding.right - right)\n this.style.right = `${right}px`\n }\n if (bottom !== undefined) {\n bottom = window.innerHeight - (bounding.bottom - bottom)\n this.style.bottom = `${bottom}px`\n }\n } else {\n if (left !== undefined) {\n this.style.left = `${left}px`\n }\n if (top !== undefined) {\n this.style.top = `${top}px`\n }\n if (right !== undefined) {\n this.style.right = `${right}px`\n }\n if (bottom !== undefined) {\n this.style.bottom = `${bottom}px`\n }\n }\n\n this.setAttribute('active', '')\n\n requestAnimationFrame(() => {\n const vh = window.innerHeight\n const vw = window.innerWidth\n\n var bounding = this.getBoundingClientRect()\n\n var h = bounding.height\n var w = bounding.width\n var t = bounding.top\n var l = bounding.left\n\n // If the popup is too large, it will cause overflow scrolling.\n if (vh < h && (top === undefined || bottom !== undefined)) {\n this.style.height = `${Math.min(Math.max(Math.floor((vh * 2) / 3), vh - (t + 20)), vh)}px`\n this.style.overflow = 'auto'\n h = vh\n }\n\n if (vw < w) {\n this.style.width = `${Math.min(Math.max(Math.floor((vw * 2) / 3), vw - (l + 20)), vw)}px`\n this.style.overflow = 'auto'\n w = vw\n }\n\n // To prevent pop-ups from crossing screen boundaries, use the\n const computedStyle = getComputedStyle(this)\n\n if (top !== undefined && bottom === undefined) {\n this.keepOffTheLine(bounding, top, anchorTop, computedStyle.top)\n } else if (t < 0) {\n this.style.top = '10px'\n this.style.bottom = ''\n } else if (vh <= t + h) {\n this.style.top = ''\n this.style.bottom = '10px'\n }\n\n if (l < 0) {\n this.style.left = `calc(${computedStyle.left} - ${l - 10}px)`\n this.style.right = ''\n } else if (vw < l + w) {\n this.style.left = `calc(${computedStyle.left} - ${l + w - vw + 10}px)`\n this.style.right = ''\n }\n })\n\n // auto focusing\n !silent && this.guaranteeFocus()\n\n /* When the window is out of focus, all pop-ups should disappear. */\n window.addEventListener('blur', this._onwindowblur)\n }\n\n /**\n * A popup opened downward from a line - the bottom of its select, or a point - never covers that line or what is\n * above it down to `anchorTop`: covered, the select's next press lands on the popup (a double-click in a grid cell\n * picked an option). With too little room below it opens above, if there is more room there; either way it is cut\n * to the room it has and scrolls inside.\n */\n private keepOffTheLine(bounding: DOMRect, top: number, anchorTop: number | undefined, styledTop: string) {\n const margin = 10\n const height = bounding.height\n const line = bounding.top\n const lineAbove = anchorTop === undefined ? line : line - (top - anchorTop)\n const below = window.innerHeight - line - margin\n const above = lineAbove - margin\n\n if (height <= below) {\n return\n }\n\n if (above > below) {\n const fitted = Math.min(height, above)\n if (fitted < height) {\n this.style.maxHeight = `${fitted}px`\n this.style.overflowY = 'auto'\n }\n this.style.top = `calc(${styledTop} - ${line - (lineAbove - fitted)}px)`\n } else {\n this.style.maxHeight = `${Math.max(below, 0)}px`\n this.style.overflowY = 'auto'\n }\n }\n\n guaranteeFocus(target?: HTMLElement) {\n const focusible = (target || this).querySelector(\n ':host > button, :host > [href], :host > input, :host > select, :host > textarea, :host > [tabindex]:not([tabindex=\"-1\"])'\n )\n\n if (focusible) {\n ;(focusible as HTMLElement).focus()\n } else {\n this.focus()\n }\n }\n\n /**\n * Closes the 'ox-popup'.\n */\n close() {\n this.removeAttribute('active')\n\n window.removeEventListener('blur', this._onwindowblur)\n\n if (this.backdrop) {\n this.removeBackdrop()\n }\n\n if (this.removeAfterUse && this.parentElement) {\n /* this case is when the popup is opened by OxPopup.open(...) */\n this.removeEventListener('focusout', this._onfocusout)\n this.removeEventListener('keydown', this._onkeydown)\n this.removeEventListener('keyup', this._onkeyup)\n this.removeEventListener('click', this._onclick)\n this.removeEventListener('ox-close', this._onclose)\n this.removeEventListener('ox-collapse', this._oncollapse)\n this.removeEventListener('mouseup', this._onmouseup)\n this.removeEventListener('mousedown', this._onmousedown)\n this.removeEventListener('contextmenu', this._oncontextmenu)\n\n this.parentElement.removeChild(this)\n\n if (this.lastActive) {\n this.lastActive.focus && this.lastActive.focus()\n }\n }\n }\n\n private createBackdrop() {\n let backdrop = document.createElement('div')\n backdrop.id = 'ox-popup-backdrop'\n backdrop.style.position = 'fixed'\n backdrop.style.top = '0'\n backdrop.style.left = '0'\n backdrop.style.width = '100vw'\n backdrop.style.height = '100vh'\n backdrop.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'\n backdrop.style.zIndex = '999'\n backdrop.style.backdropFilter = 'blur(5px)'\n backdrop.style.transition = 'opacity 0.3s ease-in-out'\n backdrop.style.opacity = '0'\n\n document.body.appendChild(backdrop)\n requestAnimationFrame(() => {\n backdrop.style.opacity = '1'\n })\n\n backdrop.addEventListener('click', () => this.close())\n }\n\n private removeBackdrop() {\n const backdrops = document.querySelectorAll('#ox-popup-backdrop')\n\n if (backdrops.length > 0) {\n const lastBackdrop = backdrops[backdrops.length - 1] as HTMLElement\n lastBackdrop.style.opacity = '0'\n setTimeout(() => {\n lastBackdrop.remove()\n }, 300)\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { OxPopup } from '../src/ox-popup.js';
|
|
4
|
+
/*
|
|
5
|
+
* A popup that clears its height when it opens (2026-10-25, so a reopened list does not keep the height it was cut to)
|
|
6
|
+
* also dropped the height its opener gave it in `style`: the fmsim board preview, asked 90vw x 90vh, opened 0 high
|
|
7
|
+
* (2026-10-28). The height asked in `style` is where every opening starts.
|
|
8
|
+
*/
|
|
9
|
+
describe('ox-popup asked size', () => {
|
|
10
|
+
let popup;
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
popup?.close();
|
|
13
|
+
popup?.remove();
|
|
14
|
+
popup = undefined;
|
|
15
|
+
});
|
|
16
|
+
it('a popup opened with a height in its style is that high', async () => {
|
|
17
|
+
popup = OxPopup.open({
|
|
18
|
+
template: html `<div style="width:100%;height:100%"></div>`,
|
|
19
|
+
style: 'width:50vw;height:50vh;'
|
|
20
|
+
});
|
|
21
|
+
await popup.updateComplete;
|
|
22
|
+
const box = popup.getBoundingClientRect();
|
|
23
|
+
expect(box.height).to.be.closeTo(window.innerHeight / 2, 1);
|
|
24
|
+
expect(box.width).to.be.closeTo(window.innerWidth / 2, 1);
|
|
25
|
+
});
|
|
26
|
+
it('opened again, it starts from the height it was asked, not from where it was cut', async () => {
|
|
27
|
+
popup = OxPopup.open({
|
|
28
|
+
template: html `<div></div>`,
|
|
29
|
+
style: 'width:200px;height:300px;'
|
|
30
|
+
});
|
|
31
|
+
await popup.updateComplete;
|
|
32
|
+
popup.style.height = '40px';
|
|
33
|
+
popup.open({});
|
|
34
|
+
await popup.updateComplete;
|
|
35
|
+
expect(popup.getBoundingClientRect().height).to.be.closeTo(300, 1);
|
|
36
|
+
});
|
|
37
|
+
it('a popup asked no height still grows with its content', async () => {
|
|
38
|
+
popup = OxPopup.open({ template: html `<div style="height:120px"></div>`, style: 'width:200px;' });
|
|
39
|
+
await popup.updateComplete;
|
|
40
|
+
expect(popup.getBoundingClientRect().height).to.be.at.least(120);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=ox-popup-asked-size.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ox-popup-asked-size.test.js","sourceRoot":"","sources":["../../test/ox-popup-asked-size.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAE1B,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAE5C;;;;GAIG;AACH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,IAAI,KAA0B,CAAA;IAE9B,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,EAAE,KAAK,EAAE,CAAA;QACd,KAAK,EAAE,MAAM,EAAE,CAAA;QACf,KAAK,GAAG,SAAS,CAAA;IACnB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;YACnB,QAAQ,EAAE,IAAI,CAAA,4CAA4C;YAC1D,KAAK,EAAE,yBAAyB;SACjC,CAAC,CAAA;QACF,MAAM,KAAK,CAAC,cAAc,CAAA;QAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,qBAAqB,EAAE,CAAA;QACzC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;QAC3D,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iFAAiF,EAAE,KAAK,IAAI,EAAE;QAC/F,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;YACnB,QAAQ,EAAE,IAAI,CAAA,aAAa;YAC3B,KAAK,EAAE,2BAA2B;SACnC,CAAC,CAAA;QACF,MAAM,KAAK,CAAC,cAAc,CAAA;QAC1B,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;QAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACd,MAAM,KAAK,CAAC,cAAc,CAAA;QAC1B,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IACpE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAA,kCAAkC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QACjG,MAAM,KAAK,CAAC,cAAc,CAAA;QAC1B,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAClE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { expect } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport { OxPopup } from '../src/ox-popup.js'\n\n/*\n * A popup that clears its height when it opens (2026-10-25, so a reopened list does not keep the height it was cut to)\n * also dropped the height its opener gave it in `style`: the fmsim board preview, asked 90vw x 90vh, opened 0 high\n * (2026-10-28). The height asked in `style` is where every opening starts.\n */\ndescribe('ox-popup asked size', () => {\n let popup: OxPopup | undefined\n\n afterEach(() => {\n popup?.close()\n popup?.remove()\n popup = undefined\n })\n\n it('a popup opened with a height in its style is that high', async () => {\n popup = OxPopup.open({\n template: html`<div style=\"width:100%;height:100%\"></div>`,\n style: 'width:50vw;height:50vh;'\n })\n await popup.updateComplete\n const box = popup.getBoundingClientRect()\n expect(box.height).to.be.closeTo(window.innerHeight / 2, 1)\n expect(box.width).to.be.closeTo(window.innerWidth / 2, 1)\n })\n\n it('opened again, it starts from the height it was asked, not from where it was cut', async () => {\n popup = OxPopup.open({\n template: html`<div></div>`,\n style: 'width:200px;height:300px;'\n })\n await popup.updateComplete\n popup.style.height = '40px'\n popup.open({})\n await popup.updateComplete\n expect(popup.getBoundingClientRect().height).to.be.closeTo(300, 1)\n })\n\n it('a popup asked no height still grows with its content', async () => {\n popup = OxPopup.open({ template: html`<div style=\"height:120px\"></div>`, style: 'width:200px;' })\n await popup.updateComplete\n expect(popup.getBoundingClientRect().height).to.be.at.least(120)\n })\n})\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '../src/ox-popup-list.js';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { expect, fixture } from '@open-wc/testing';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import '../src/ox-popup-list.js';
|
|
4
|
+
/**
|
|
5
|
+
* A searchable list is a select with a search box on top. It opened with the focus on an option, so the user had to
|
|
6
|
+
* click the box before typing, and Enter in the box did nothing.
|
|
7
|
+
*/
|
|
8
|
+
describe('ox-popup-list with search', () => {
|
|
9
|
+
const tick = () => new Promise(resolve => setTimeout(resolve, 10));
|
|
10
|
+
async function opened() {
|
|
11
|
+
const list = await fixture(html `<ox-popup-list with-search>
|
|
12
|
+
<div option value="a">Alpha</div>
|
|
13
|
+
<div option value="m1">MES one</div>
|
|
14
|
+
<div option value="m2">MES two</div>
|
|
15
|
+
</ox-popup-list>`);
|
|
16
|
+
list.open({ left: 0, top: 0 });
|
|
17
|
+
await list.updateComplete;
|
|
18
|
+
await tick();
|
|
19
|
+
return list;
|
|
20
|
+
}
|
|
21
|
+
const type = async (list, text) => {
|
|
22
|
+
list.searchInput.value = text;
|
|
23
|
+
list.searchInput.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
|
|
24
|
+
await list.updateComplete;
|
|
25
|
+
};
|
|
26
|
+
const enter = (list, init = {}) => list.searchInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, composed: true, ...init }));
|
|
27
|
+
it('opens with the focus in the search box', async () => {
|
|
28
|
+
const list = await opened();
|
|
29
|
+
/* Compared as a boolean: a failed chai match on an element hangs the runner. */
|
|
30
|
+
expect(list.shadowRoot.activeElement === list.searchInput).to.equal(true);
|
|
31
|
+
});
|
|
32
|
+
it('picks the first match on Enter in the search box', async () => {
|
|
33
|
+
const list = await opened();
|
|
34
|
+
await type(list, 'mes');
|
|
35
|
+
let picked;
|
|
36
|
+
list.addEventListener('select', (e) => (picked = e.detail));
|
|
37
|
+
enter(list);
|
|
38
|
+
await tick();
|
|
39
|
+
expect(picked).to.equal('m1');
|
|
40
|
+
});
|
|
41
|
+
it('leaves Enter alone while a word is being composed', async () => {
|
|
42
|
+
const list = await opened();
|
|
43
|
+
await type(list, 'mes');
|
|
44
|
+
let picked = false;
|
|
45
|
+
list.addEventListener('select', () => (picked = true));
|
|
46
|
+
enter(list, { isComposing: true });
|
|
47
|
+
await tick();
|
|
48
|
+
expect(picked).to.equal(false);
|
|
49
|
+
});
|
|
50
|
+
it('clears the last search when it opens again', async () => {
|
|
51
|
+
const list = await opened();
|
|
52
|
+
await type(list, 'mes');
|
|
53
|
+
list.close();
|
|
54
|
+
list.open({ left: 0, top: 0 });
|
|
55
|
+
await list.updateComplete;
|
|
56
|
+
await tick();
|
|
57
|
+
expect(list.searchInput.value).to.equal('');
|
|
58
|
+
expect(list.querySelectorAll('[option]:not([hidden])').length).to.equal(3);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=ox-popup-list-search.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ox-popup-list-search.test.js","sourceRoot":"","sources":["../../test/ox-popup-list-search.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport '../src/ox-popup-list.js'\nimport type { OxPopupList } from '../src/ox-popup-list.js'\n\n/**\n * A searchable list is a select with a search box on top. It opened with the focus on an option, so the user had to\n * click the box before typing, and Enter in the box did nothing.\n */\ndescribe('ox-popup-list with search', () => {\n const tick = () => new Promise(resolve => setTimeout(resolve, 10))\n\n async function opened() {\n const list = await fixture<OxPopupList>(html`<ox-popup-list with-search>\n <div option value=\"a\">Alpha</div>\n <div option value=\"m1\">MES one</div>\n <div option value=\"m2\">MES two</div>\n </ox-popup-list>`)\n list.open({ left: 0, top: 0 })\n await list.updateComplete\n await tick()\n return list\n }\n\n const type = async (list: OxPopupList, text: string) => {\n list.searchInput.value = text\n list.searchInput.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }))\n await list.updateComplete\n }\n\n const enter = (list: OxPopupList, init: KeyboardEventInit = {}) =>\n list.searchInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, composed: true, ...init }))\n\n it('opens with the focus in the search box', async () => {\n const list = await opened()\n /* Compared as a boolean: a failed chai match on an element hangs the runner. */\n expect(list.shadowRoot!.activeElement === list.searchInput).to.equal(true)\n })\n\n it('picks the first match on Enter in the search box', async () => {\n const list = await opened()\n await type(list, 'mes')\n let picked: unknown\n list.addEventListener('select', (e: Event) => (picked = (e as CustomEvent).detail))\n enter(list)\n await tick()\n expect(picked).to.equal('m1')\n })\n\n it('leaves Enter alone while a word is being composed', async () => {\n const list = await opened()\n await type(list, 'mes')\n let picked = false\n list.addEventListener('select', () => (picked = true))\n enter(list, { isComposing: true })\n await tick()\n expect(picked).to.equal(false)\n })\n\n it('clears the last search when it opens again', async () => {\n const list = await opened()\n await type(list, 'mes')\n list.close()\n list.open({ left: 0, top: 0 })\n await list.updateComplete\n await tick()\n expect(list.searchInput.value).to.equal('')\n expect(list.querySelectorAll('[option]:not([hidden])').length).to.equal(3)\n })\n})\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '../src/ox-popup-list.js';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
|
+
import { html, render } from 'lit';
|
|
3
|
+
import '../src/ox-popup-list.js';
|
|
4
|
+
/*
|
|
5
|
+
* A list taller than the room below its select was moved up over the select itself (2026-10-25). In a grid cell the
|
|
6
|
+
* second press of a double-click then landed on an option and picked it. A popup stays off the line it opens from: it
|
|
7
|
+
* shrinks to the room below, or opens above when there is more room there.
|
|
8
|
+
*/
|
|
9
|
+
describe('ox-popup placement', () => {
|
|
10
|
+
const frame = () => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
|
11
|
+
let list;
|
|
12
|
+
async function open(count, place) {
|
|
13
|
+
const host = document.createElement('div');
|
|
14
|
+
document.body.appendChild(host);
|
|
15
|
+
render(html `<ox-popup-list with-search>
|
|
16
|
+
${Array.from({ length: count }, (_, i) => html `<div option value=${i}>Option ${i}</div>`)}
|
|
17
|
+
</ox-popup-list>`, host);
|
|
18
|
+
list = host.querySelector('ox-popup-list');
|
|
19
|
+
document.body.appendChild(list);
|
|
20
|
+
host.remove();
|
|
21
|
+
await list.updateComplete;
|
|
22
|
+
list.open({ left: 20, ...place });
|
|
23
|
+
await list.updateComplete;
|
|
24
|
+
await frame();
|
|
25
|
+
return list.getBoundingClientRect();
|
|
26
|
+
}
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
list?.close();
|
|
29
|
+
list?.remove();
|
|
30
|
+
});
|
|
31
|
+
it('a long list under a select with more room below shrinks to that room and keeps its search box', async () => {
|
|
32
|
+
const anchorTop = Math.round(window.innerHeight / 3);
|
|
33
|
+
const box = await open(200, { anchorTop, top: anchorTop + 30 });
|
|
34
|
+
expect(box.top).to.be.at.least(anchorTop + 30 - 0.5);
|
|
35
|
+
expect(box.bottom).to.be.at.most(window.innerHeight + 0.5);
|
|
36
|
+
const search = list.shadowRoot.querySelector('[search]').getBoundingClientRect();
|
|
37
|
+
expect(search.top).to.be.at.least(anchorTop + 30 - 0.5);
|
|
38
|
+
const last = list.querySelector('[option]:last-of-type');
|
|
39
|
+
expect(last.getBoundingClientRect().bottom > box.bottom).to.equal(true);
|
|
40
|
+
});
|
|
41
|
+
it('a long list under a select near the bottom opens above the select', async () => {
|
|
42
|
+
const anchorTop = window.innerHeight - 60;
|
|
43
|
+
const box = await open(200, { anchorTop, top: anchorTop + 30 });
|
|
44
|
+
expect(box.bottom).to.be.at.most(anchorTop + 0.5);
|
|
45
|
+
expect(box.top).to.be.at.least(-0.5);
|
|
46
|
+
});
|
|
47
|
+
it('a short list that fits stays where it was asked to open', async () => {
|
|
48
|
+
const box = await open(3, { anchorTop: 40, top: 70 });
|
|
49
|
+
expect(box.top).to.be.closeTo(70, 0.5);
|
|
50
|
+
});
|
|
51
|
+
it('a menu at a point near the bottom opens above the point, not over it', async () => {
|
|
52
|
+
const point = window.innerHeight - 20;
|
|
53
|
+
const box = await open(30, { top: point });
|
|
54
|
+
expect(box.bottom).to.be.at.most(point + 0.5);
|
|
55
|
+
});
|
|
56
|
+
it('opened again elsewhere, it does not keep the last place or the last cut', async () => {
|
|
57
|
+
const anchorTop = Math.round(window.innerHeight / 2);
|
|
58
|
+
await open(200, { anchorTop, top: anchorTop + 30 });
|
|
59
|
+
list.close();
|
|
60
|
+
list.open({ left: 20, anchorTop: 40, top: 70 });
|
|
61
|
+
await list.updateComplete;
|
|
62
|
+
await frame();
|
|
63
|
+
const box = list.getBoundingClientRect();
|
|
64
|
+
expect(box.top).to.be.closeTo(70, 0.5);
|
|
65
|
+
/* The room below now, less the margin - not the shorter cut of the last opening. */
|
|
66
|
+
expect(box.bottom).to.be.closeTo(window.innerHeight - 10, 0.5);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=ox-popup-placement.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ox-popup-placement.test.js","sourceRoot":"","sources":["../../test/ox-popup-placement.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing'\nimport { html, render } from 'lit'\n\nimport '../src/ox-popup-list.js'\nimport type { OxPopupList } from '../src/ox-popup-list.js'\n\n/*\n * A list taller than the room below its select was moved up over the select itself (2026-10-25). In a grid cell the\n * second press of a double-click then landed on an option and picked it. A popup stays off the line it opens from: it\n * shrinks to the room below, or opens above when there is more room there.\n */\ndescribe('ox-popup placement', () => {\n const frame = () => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))\n let list: OxPopupList\n\n async function open(count: number, place: { top: number; anchorTop?: number }) {\n const host = document.createElement('div')\n document.body.appendChild(host)\n render(\n html`<ox-popup-list with-search>\n ${Array.from({ length: count }, (_, i) => html`<div option value=${i}>Option ${i}</div>`)}\n </ox-popup-list>`,\n host\n )\n list = host.querySelector('ox-popup-list') as OxPopupList\n document.body.appendChild(list)\n host.remove()\n await list.updateComplete\n list.open({ left: 20, ...place })\n await list.updateComplete\n await frame()\n return list.getBoundingClientRect()\n }\n\n afterEach(() => {\n list?.close()\n list?.remove()\n })\n\n it('a long list under a select with more room below shrinks to that room and keeps its search box', async () => {\n const anchorTop = Math.round(window.innerHeight / 3)\n const box = await open(200, { anchorTop, top: anchorTop + 30 })\n expect(box.top).to.be.at.least(anchorTop + 30 - 0.5)\n expect(box.bottom).to.be.at.most(window.innerHeight + 0.5)\n const search = list.shadowRoot!.querySelector('[search]')!.getBoundingClientRect()\n expect(search.top).to.be.at.least(anchorTop + 30 - 0.5)\n const last = list.querySelector('[option]:last-of-type')!\n expect(last.getBoundingClientRect().bottom > box.bottom).to.equal(true)\n })\n\n it('a long list under a select near the bottom opens above the select', async () => {\n const anchorTop = window.innerHeight - 60\n const box = await open(200, { anchorTop, top: anchorTop + 30 })\n expect(box.bottom).to.be.at.most(anchorTop + 0.5)\n expect(box.top).to.be.at.least(-0.5)\n })\n\n it('a short list that fits stays where it was asked to open', async () => {\n const box = await open(3, { anchorTop: 40, top: 70 })\n expect(box.top).to.be.closeTo(70, 0.5)\n })\n\n it('a menu at a point near the bottom opens above the point, not over it', async () => {\n const point = window.innerHeight - 20\n const box = await open(30, { top: point })\n expect(box.bottom).to.be.at.most(point + 0.5)\n })\n\n it('opened again elsewhere, it does not keep the last place or the last cut', async () => {\n const anchorTop = Math.round(window.innerHeight / 2)\n await open(200, { anchorTop, top: anchorTop + 30 })\n list.close()\n list.open({ left: 20, anchorTop: 40, top: 70 })\n await list.updateComplete\n await frame()\n const box = list.getBoundingClientRect()\n expect(box.top).to.be.closeTo(70, 0.5)\n /* The room below now, less the margin - not the shorter cut of the last opening. */\n expect(box.bottom).to.be.closeTo(window.innerHeight - 10, 0.5)\n })\n})\n"]}
|