@aquera/nile-elements 1.1.3-beta-1.4 → 1.1.3-beta-2.1

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.
Files changed (111) hide show
  1. package/demo/index.html +124 -24
  2. package/dist/index.js +729 -770
  3. package/dist/nile-grid/nile-grid.cjs.js +1 -1
  4. package/dist/nile-grid/nile-grid.cjs.js.map +1 -1
  5. package/dist/nile-grid/nile-grid.css.cjs.js +1 -1
  6. package/dist/nile-grid/nile-grid.css.cjs.js.map +1 -1
  7. package/dist/nile-grid/nile-grid.css.esm.js +4 -8
  8. package/dist/nile-grid/nile-grid.esm.js +6 -6
  9. package/dist/nile-grid/nile-grid.utils.cjs.js +1 -1
  10. package/dist/nile-grid/nile-grid.utils.cjs.js.map +1 -1
  11. package/dist/nile-grid/nile-grid.utils.esm.js +1 -1
  12. package/dist/nile-grid-body/nile-grid-body.cjs.js +1 -1
  13. package/dist/nile-grid-body/nile-grid-body.cjs.js.map +1 -1
  14. package/dist/nile-grid-body/nile-grid-body.css.cjs.js +1 -1
  15. package/dist/nile-grid-body/nile-grid-body.css.cjs.js.map +1 -1
  16. package/dist/nile-grid-body/nile-grid-body.css.esm.js +4 -0
  17. package/dist/nile-grid-body/nile-grid-body.esm.js +3 -3
  18. package/dist/nile-grid-cell-item/nile-grid-cell-item.cjs.js +1 -1
  19. package/dist/nile-grid-cell-item/nile-grid-cell-item.cjs.js.map +1 -1
  20. package/dist/nile-grid-cell-item/nile-grid-cell-item.css.cjs.js +1 -1
  21. package/dist/nile-grid-cell-item/nile-grid-cell-item.css.cjs.js.map +1 -1
  22. package/dist/nile-grid-cell-item/nile-grid-cell-item.css.esm.js +3 -18
  23. package/dist/nile-grid-cell-item/nile-grid-cell-item.esm.js +3 -3
  24. package/dist/nile-grid-head/nile-grid-head.cjs.js +1 -1
  25. package/dist/nile-grid-head/nile-grid-head.cjs.js.map +1 -1
  26. package/dist/nile-grid-head/nile-grid-head.css.cjs.js +1 -1
  27. package/dist/nile-grid-head/nile-grid-head.css.cjs.js.map +1 -1
  28. package/dist/nile-grid-head/nile-grid-head.css.esm.js +1 -1
  29. package/dist/nile-grid-head/nile-grid-head.esm.js +3 -3
  30. package/dist/nile-grid-head-item/nile-grid-head-item.cjs.js +1 -1
  31. package/dist/nile-grid-head-item/nile-grid-head-item.cjs.js.map +1 -1
  32. package/dist/nile-grid-head-item/nile-grid-head-item.css.cjs.js +1 -1
  33. package/dist/nile-grid-head-item/nile-grid-head-item.css.cjs.js.map +1 -1
  34. package/dist/nile-grid-head-item/nile-grid-head-item.css.esm.js +5 -20
  35. package/dist/nile-grid-head-item/nile-grid-head-item.esm.js +3 -4
  36. package/dist/nile-grid-row/nile-grid-row.cjs.js +1 -1
  37. package/dist/nile-grid-row/nile-grid-row.cjs.js.map +1 -1
  38. package/dist/nile-grid-row/nile-grid-row.css.cjs.js +1 -1
  39. package/dist/nile-grid-row/nile-grid-row.css.cjs.js.map +1 -1
  40. package/dist/nile-grid-row/nile-grid-row.css.esm.js +5 -17
  41. package/dist/nile-grid-row/nile-grid-row.esm.js +5 -4
  42. package/dist/nile-select/nile-select.cjs.js +1 -1
  43. package/dist/nile-select/nile-select.cjs.js.map +1 -1
  44. package/dist/nile-select/nile-select.esm.js +2 -2
  45. package/dist/nile-select/virtual-scroll-helper.cjs.js +1 -1
  46. package/dist/nile-select/virtual-scroll-helper.cjs.js.map +1 -1
  47. package/dist/nile-select/virtual-scroll-helper.esm.js +1 -0
  48. package/dist/nile-virtual-select/nile-virtual-select.cjs.js +2 -2
  49. package/dist/nile-virtual-select/nile-virtual-select.cjs.js.map +1 -1
  50. package/dist/nile-virtual-select/nile-virtual-select.esm.js +7 -6
  51. package/dist/src/nile-grid/nile-grid.css.js +2 -6
  52. package/dist/src/nile-grid/nile-grid.css.js.map +1 -1
  53. package/dist/src/nile-grid/nile-grid.d.ts +3 -9
  54. package/dist/src/nile-grid/nile-grid.js +20 -96
  55. package/dist/src/nile-grid/nile-grid.js.map +1 -1
  56. package/dist/src/nile-grid/nile-grid.utils.d.ts +2 -3
  57. package/dist/src/nile-grid/nile-grid.utils.js +81 -223
  58. package/dist/src/nile-grid/nile-grid.utils.js.map +1 -1
  59. package/dist/src/nile-grid-body/nile-grid-body.css.js +4 -0
  60. package/dist/src/nile-grid-body/nile-grid-body.css.js.map +1 -1
  61. package/dist/src/nile-grid-body/nile-grid-body.js +1 -1
  62. package/dist/src/nile-grid-body/nile-grid-body.js.map +1 -1
  63. package/dist/src/nile-grid-cell-item/nile-grid-cell-item.css.js +2 -17
  64. package/dist/src/nile-grid-cell-item/nile-grid-cell-item.css.js.map +1 -1
  65. package/dist/src/nile-grid-cell-item/nile-grid-cell-item.d.ts +3 -0
  66. package/dist/src/nile-grid-cell-item/nile-grid-cell-item.js +11 -1
  67. package/dist/src/nile-grid-cell-item/nile-grid-cell-item.js.map +1 -1
  68. package/dist/src/nile-grid-head/nile-grid-head.css.js +1 -1
  69. package/dist/src/nile-grid-head/nile-grid-head.css.js.map +1 -1
  70. package/dist/src/nile-grid-head/nile-grid-head.js +1 -1
  71. package/dist/src/nile-grid-head/nile-grid-head.js.map +1 -1
  72. package/dist/src/nile-grid-head-item/nile-grid-head-item.css.js +3 -18
  73. package/dist/src/nile-grid-head-item/nile-grid-head-item.css.js.map +1 -1
  74. package/dist/src/nile-grid-head-item/nile-grid-head-item.d.ts +2 -3
  75. package/dist/src/nile-grid-head-item/nile-grid-head-item.js +11 -14
  76. package/dist/src/nile-grid-head-item/nile-grid-head-item.js.map +1 -1
  77. package/dist/src/nile-grid-row/nile-grid-row.css.js +5 -17
  78. package/dist/src/nile-grid-row/nile-grid-row.css.js.map +1 -1
  79. package/dist/src/nile-grid-row/nile-grid-row.d.ts +1 -4
  80. package/dist/src/nile-grid-row/nile-grid-row.js +4 -19
  81. package/dist/src/nile-grid-row/nile-grid-row.js.map +1 -1
  82. package/dist/src/nile-select/nile-select.d.ts +1 -0
  83. package/dist/src/nile-select/nile-select.js +4 -0
  84. package/dist/src/nile-select/nile-select.js.map +1 -1
  85. package/dist/src/nile-select/virtual-scroll-helper.js +1 -0
  86. package/dist/src/nile-select/virtual-scroll-helper.js.map +1 -1
  87. package/dist/src/nile-virtual-select/nile-virtual-select.d.ts +18 -0
  88. package/dist/src/nile-virtual-select/nile-virtual-select.js +230 -3
  89. package/dist/src/nile-virtual-select/nile-virtual-select.js.map +1 -1
  90. package/dist/src/version.js +1 -1
  91. package/dist/src/version.js.map +1 -1
  92. package/dist/tsconfig.tsbuildinfo +1 -1
  93. package/package.json +1 -1
  94. package/src/nile-grid/nile-grid.css.ts +2 -6
  95. package/src/nile-grid/nile-grid.ts +21 -124
  96. package/src/nile-grid/nile-grid.utils.ts +98 -353
  97. package/src/nile-grid-body/nile-grid-body.css.ts +4 -0
  98. package/src/nile-grid-body/nile-grid-body.ts +2 -2
  99. package/src/nile-grid-cell-item/nile-grid-cell-item.css.ts +2 -17
  100. package/src/nile-grid-cell-item/nile-grid-cell-item.ts +11 -3
  101. package/src/nile-grid-head/nile-grid-head.css.ts +1 -1
  102. package/src/nile-grid-head/nile-grid-head.ts +2 -2
  103. package/src/nile-grid-head-item/nile-grid-head-item.css.ts +3 -18
  104. package/src/nile-grid-head-item/nile-grid-head-item.ts +16 -8
  105. package/src/nile-grid-row/nile-grid-row.css.ts +5 -17
  106. package/src/nile-grid-row/nile-grid-row.ts +6 -9
  107. package/src/nile-select/nile-select.ts +2 -0
  108. package/src/nile-select/virtual-scroll-helper.ts +1 -0
  109. package/src/nile-virtual-select/nile-virtual-select.ts +257 -3
  110. package/vscode-html-custom-data.json +75 -38
  111. package/src/nile-grid/nile-grid.types.ts +0 -1
@@ -12,8 +12,7 @@ import { css } from 'lit';
12
12
  */
13
13
  export const styles = css`
14
14
  :host {
15
- --padding: var(--nile-spacing-lg) var(--nile-spacing-3xl);
16
- --height: 50px;
15
+ --padding: var(--nile-spacing-xl) var(--nile-spacing-3xl);
17
16
  position: relative;
18
17
  box-sizing: border-box;
19
18
  user-select: none;
@@ -24,16 +23,7 @@ export const styles = css`
24
23
  padding: var(--padding);
25
24
  background-color: var(--nile-colors-neutral-100);
26
25
  border-bottom: 1px solid var(--nile-colors-neutral-400);
27
- font-size: 12px;
28
- font-weight: 500;
29
- font-family: var(--nile-font-weight-medium);
30
- min-height: var(--height);
31
- }
32
-
33
- .head-item-base {
34
- width: 100%;
35
- overflow: hidden;
36
- text-overflow: ellipsis;
26
+ border-right: 1px solid var(--nile-colors-neutral-400);
37
27
  }
38
28
 
39
29
  ::slotted(*) {
@@ -42,17 +32,12 @@ export const styles = css`
42
32
  white-space: nowrap;
43
33
  }
44
34
 
45
- :host(:last-child) .resize-handle {
46
- border-right: none;
47
- display: none;
48
- }
49
-
50
35
  .resize-handle {
51
36
  position: absolute;
52
37
  top: 0;
53
38
  right: 0;
54
39
  height: 100%;
55
- cursor: ew-resize;
40
+ cursor: col-resize;
56
41
  touch-action: none;
57
42
  z-index: 1;
58
43
  width: 2px;
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import { html, CSSResultArray, TemplateResult } from 'lit';
8
+ import { LitElement, html, CSSResultArray, TemplateResult } from 'lit';
9
9
  import { customElement, property } from 'lit/decorators.js';
10
10
  import { styles } from './nile-grid-head-item.css';
11
11
  import NileElement from '../internal/nile-element';
@@ -33,14 +33,18 @@ export class NileGridHeadItem extends NileElement {
33
33
 
34
34
  @property({ type: Number, reflect: true, attribute: true }) colspan = 1;
35
35
  @property({ type: Number, reflect: true, attribute: true }) rowspan = 1;
36
+ @property({ type: String, reflect: true, attribute: true }) scope:
37
+ | 'col'
38
+ | 'row' = 'col';
36
39
  @property({ type: Boolean, reflect: true, attribute: true }) resizable = false;
37
- @property({ type: Boolean, reflect: true, attribute: true }) colSticky = false;
38
- @property({ type: Boolean, reflect: true, attribute: true }) lastColSticky = false;
39
- @property({ type: Number, reflect: true, attribute: true }) customWidth: number | null = null;
40
+ @property({ type: Boolean, reflect: true, attribute: true }) sticky = false;
40
41
 
41
42
  connectedCallback() {
42
43
  super.connectedCallback();
43
- this.setAttribute('role','row');
44
+ this.setAttribute(
45
+ 'role',
46
+ this.scope === 'row' ? 'rowheader' : 'columnheader'
47
+ );
44
48
  }
45
49
 
46
50
  private onHandlePointerDown = (ev: PointerEvent) => {
@@ -65,7 +69,12 @@ export class NileGridHeadItem extends NileElement {
65
69
  };
66
70
 
67
71
  protected updated(changed: Map<string, unknown>) {
68
- this.setAttribute('role','row');
72
+ if (changed.has('scope')) {
73
+ this.setAttribute(
74
+ 'role',
75
+ this.scope === 'row' ? 'rowheader' : 'columnheader'
76
+ );
77
+ }
69
78
  }
70
79
 
71
80
  disconnectedCallback() {
@@ -75,14 +84,13 @@ export class NileGridHeadItem extends NileElement {
75
84
 
76
85
  public render(): TemplateResult {
77
86
  return html`
78
- <div part="grid-head-item-base" class="head-item-base">
87
+ <div style="display: contents;">
79
88
  <slot></slot>
80
89
  ${this.resizable
81
90
  ? html`
82
91
  <div
83
92
  aria-hidden="false"
84
93
  class="resize-handle"
85
- part="resize-handle"
86
94
  @pointerdown=${this.onHandlePointerDown}
87
95
  ></div>
88
96
  `
@@ -12,30 +12,18 @@ import { css } from 'lit';
12
12
  */
13
13
  export const styles = css`
14
14
  :host {
15
- display: grid;
16
- grid-template-columns: var(--nile-grid-columns);
17
- position: relative;
15
+ display: contents;
18
16
  }
19
17
 
20
- :host(:hover) {
18
+ ::slotted(nile-grid-header) {
21
19
  background-color: var(--nile-colors-neutral-100);
22
20
  }
23
21
 
24
- .row-bg {
25
- grid-column: 1 / -1;
26
- pointer-events: none;
22
+ .row-layer {
23
+ background-color: var(--row-bg, transparent);
27
24
  z-index: 0;
28
- display: none;
25
+ pointer-events: auto;
29
26
  }
30
-
31
- .row-slot {
32
- grid-column: 1 / -1;
33
- display: contents;
34
- z-index: 1;
35
- }
36
-
37
- :host([selected]) .row-bg { background: var(--row-selected-bg, rgba(0,0,0,.08)); }
38
- :host([error]) .row-bg { background: var(--row-error-bg, #fff4f4); }
39
27
  `;
40
28
 
41
29
  export default [styles];
@@ -5,8 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import { html, CSSResultArray, TemplateResult, PropertyValues } from 'lit';
9
- import { customElement, property } from 'lit/decorators.js';
8
+ import { LitElement, html, CSSResultArray, TemplateResult } from 'lit';
9
+ import { customElement } from 'lit/decorators.js';
10
10
  import { styles } from './nile-grid-row.css';
11
11
  import NileElement from '../internal/nile-element';
12
12
 
@@ -26,21 +26,18 @@ export class NileGridRow extends NileElement {
26
26
  return [styles];
27
27
  }
28
28
 
29
- @property({ type: Boolean, reflect: true, attribute: true }) disabled: boolean = false;
30
- @property({ type: Boolean, reflect: true, attribute: true }) hover: boolean = false;
31
- @property({ type: Boolean, reflect: true, attribute: true }) selected: boolean = false;
32
-
33
29
  connectedCallback() {
34
30
  super.connectedCallback();
35
31
  this.setAttribute('role', 'row');
36
32
  }
37
33
 
38
- protected render() {
34
+ public render(): TemplateResult {
39
35
  return html`
40
- <div part="row-slot" class="row-slot">
36
+ <div part="row-layer" class="row-layer" data-row-bg></div>
37
+
38
+ <div style="display: contents;">
41
39
  <slot></slot>
42
40
  </div>
43
- <div part="row-bg" class="row-bg"></div>
44
41
  `;
45
42
  }
46
43
 
@@ -174,6 +174,8 @@ export class NileSelect extends NileElement implements NileFormControl{
174
174
 
175
175
  @property({ type: Boolean, reflect: true }) noWidthSync = false;
176
176
 
177
+ @property({ type: Boolean, reflect: true }) bodyAppend = false;
178
+
177
179
  /** Allows more than one option to be selected. */
178
180
  @property({
179
181
  reflect: true,
@@ -50,6 +50,7 @@ export class VirtualScrollHelper {
50
50
  .data=${component.data}
51
51
  .open=${component.open}
52
52
  .loading=${component.loading}
53
+ .bodyAppend=${component.bodyAppend}
53
54
  exportparts="
54
55
  select-options,
55
56
  select-no-results,
@@ -247,6 +247,12 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
247
247
  /** Disable width synchronization */
248
248
  @property({ type: Boolean, reflect: true }) noWidthSync = false;
249
249
 
250
+ /**
251
+ * When true, the listbox will be appended to the document body instead of the parent container.
252
+ * This is useful when the parent has overflow: hidden, clip-path, or transform applied.
253
+ */
254
+ @property({ type: Boolean, reflect: true }) bodyAppend = false;
255
+
250
256
  /**
251
257
  * The maximum number of selected options to show when `multiple` is true. After the maximum, "+n" will be shown to
252
258
  * indicate the number of additional items that are selected. Set to 0 to remove the limit.
@@ -256,6 +262,10 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
256
262
 
257
263
  @state() oldMaxOptionsVisible: number = 1;
258
264
  @state() showListbox: boolean = false;
265
+
266
+ // Body append-related state
267
+ @state() private bodyAppendContainer: HTMLElement | null = null;
268
+ @state() private bodyAppendListbox: HTMLElement | null = null;
259
269
 
260
270
  /** Gets the validity state object */
261
271
  get validity() {
@@ -286,6 +296,8 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
286
296
  clearTimeout(this.scrollTimeout);
287
297
  this.scrollTimeout = undefined;
288
298
  }
299
+ // Clean up body append elements
300
+ this.cleanupBodyAppend();
289
301
  }
290
302
 
291
303
  protected updated(changedProperties: PropertyValues): void {
@@ -343,6 +355,8 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
343
355
  this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
344
356
  this.handleDocumentMouseDown = this.handleDocumentMouseDown.bind(this);
345
357
  this.handleWindowError = this.handleWindowError.bind(this);
358
+ this.handleWindowResize = this.handleWindowResize.bind(this);
359
+ this.handleWindowScroll = this.handleWindowScroll.bind(this);
346
360
  }
347
361
 
348
362
  private addOpenListeners(): void {
@@ -350,6 +364,11 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
350
364
  document.addEventListener('keydown', this.handleDocumentKeyDown);
351
365
  document.addEventListener('mousedown', this.handleDocumentMouseDown);
352
366
  window.addEventListener('error', this.handleWindowError);
367
+
368
+ if (this.bodyAppend) {
369
+ window.addEventListener('resize', this.handleWindowResize);
370
+ window.addEventListener('scroll', this.handleWindowScroll, true);
371
+ }
353
372
  }
354
373
 
355
374
  private removeOpenListeners(): void {
@@ -357,6 +376,8 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
357
376
  document.removeEventListener('keydown', this.handleDocumentKeyDown);
358
377
  document.removeEventListener('mousedown', this.handleDocumentMouseDown);
359
378
  window.removeEventListener('error', this.handleWindowError);
379
+ window.removeEventListener('resize', this.handleWindowResize);
380
+ window.removeEventListener('scroll', this.handleWindowScroll, true);
360
381
  }
361
382
 
362
383
  private handleFocus(): void {
@@ -374,8 +395,9 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
374
395
  const path = event.composedPath();
375
396
  const hitSelf = path.includes(this);
376
397
  const hitPopup = this.popup && path.includes(this.popup);
398
+ const hitBodyAppend = this.bodyAppend && this.bodyAppendContainer && path.includes(this.bodyAppendContainer);
377
399
 
378
- if (!hitSelf && !hitPopup) {
400
+ if (!hitSelf && !hitPopup && !hitBodyAppend) {
379
401
  this.hide();
380
402
  }
381
403
  }
@@ -437,8 +459,9 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
437
459
  const path = event.composedPath();
438
460
  const hitSelf = path.includes(this);
439
461
  const hitPopup = this.popup && path.includes(this.popup);
462
+ const hitBodyAppend = this.bodyAppend && this.bodyAppendContainer && path.includes(this.bodyAppendContainer);
440
463
 
441
- if (!hitSelf && !hitPopup) {
464
+ if (!hitSelf && !hitPopup && !hitBodyAppend) {
442
465
  this.hide();
443
466
  }
444
467
  };
@@ -456,6 +479,14 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
456
479
  }
457
480
  };
458
481
 
482
+ private handleWindowResize = (): void => {
483
+ this.updateBodyAppendPosition();
484
+ };
485
+
486
+ private handleWindowScroll = (): void => {
487
+ this.updateBodyAppendPosition();
488
+ };
489
+
459
490
  private handleFooterClick(event: MouseEvent): void {
460
491
  event.stopPropagation();
461
492
  event.preventDefault();
@@ -807,6 +838,11 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
807
838
  this.showNoResults = false;
808
839
  }
809
840
  this.requestUpdate();
841
+
842
+ // Update body append content if it's active
843
+ if (this.bodyAppend && this.bodyAppendContainer) {
844
+ this.updateBodyAppendContent();
845
+ }
810
846
  }
811
847
 
812
848
  @watch('renderItemConfig', { waitUntilFirstUpdate: true })
@@ -824,6 +860,22 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
824
860
  this.showNoResults = true;
825
861
  }
826
862
  this.requestUpdate();
863
+
864
+ // Update body append content if it's active
865
+ if (this.bodyAppend && this.bodyAppendContainer) {
866
+ this.updateBodyAppendContent();
867
+ }
868
+ }
869
+
870
+ @watch('bodyAppend', { waitUntilFirstUpdate: true })
871
+ handleBodyAppendChange(): void {
872
+ if (this.open) {
873
+ if (this.bodyAppend) {
874
+ this.setupBodyAppend();
875
+ } else {
876
+ this.cleanupBodyAppend();
877
+ }
878
+ }
827
879
  }
828
880
 
829
881
  @watch('open', { waitUntilFirstUpdate: true })
@@ -831,9 +883,17 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
831
883
  if (this.open && !this.disabled) {
832
884
  await this.handleOpen();
833
885
  this.showListbox = true;
886
+
887
+ if (this.bodyAppend) {
888
+ this.setupBodyAppend();
889
+ }
834
890
  } else {
835
891
  await this.handleClose();
836
892
  this.showListbox = false;
893
+
894
+ if (this.bodyAppend) {
895
+ this.cleanupBodyAppend();
896
+ }
837
897
  }
838
898
  }
839
899
 
@@ -1219,9 +1279,10 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
1219
1279
  part="listbox"
1220
1280
  class="select__listbox ${this.searchEnabled
1221
1281
  ? 'select__search-enabled '
1222
- : ''}"
1282
+ : ''} ${this.bodyAppend ? 'select__body-append-hidden' : ''}"
1223
1283
  tabindex="-1"
1224
1284
  @mouseup=${this.handleOptionClick}
1285
+ style=${this.bodyAppend ? 'display: none;' : ''}
1225
1286
  >
1226
1287
  ${this.renderSearch()}
1227
1288
  ${this.renderLoader()}
@@ -1425,6 +1486,199 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
1425
1486
  }
1426
1487
  });
1427
1488
  }
1489
+
1490
+ private createBodyAppendContainer(): HTMLElement {
1491
+ const container = document.createElement('div');
1492
+ container.style.position = 'absolute';
1493
+ container.style.zIndex = '9999';
1494
+ container.style.pointerEvents = 'none';
1495
+ container.className = 'nile-virtual-select-body-append';
1496
+
1497
+ // Apply additional styling to match the original popup
1498
+ container.style.background = 'white';
1499
+ container.style.border = '1px solid #e0e0e0';
1500
+ container.style.borderRadius = '4px';
1501
+ container.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
1502
+ container.style.maxHeight = '300px';
1503
+ container.style.overflow = 'hidden';
1504
+
1505
+ return container;
1506
+ }
1507
+
1508
+ private createBodyAppendListbox(): HTMLElement {
1509
+ const listbox = document.createElement('div');
1510
+ listbox.id = 'listbox';
1511
+ listbox.setAttribute('role', 'listbox');
1512
+ listbox.setAttribute('aria-expanded', this.open ? 'true' : 'false');
1513
+ listbox.setAttribute('aria-multiselectable', this.multiple ? 'true' : 'false');
1514
+ listbox.setAttribute('aria-labelledby', 'label');
1515
+ listbox.setAttribute('part', 'listbox');
1516
+ listbox.className = `select__listbox ${this.searchEnabled ? 'select__search-enabled ' : ''}`;
1517
+ listbox.setAttribute('tabindex', '-1');
1518
+
1519
+ // Apply styling to match the original listbox
1520
+ listbox.style.background = 'white';
1521
+ listbox.style.border = 'none';
1522
+ listbox.style.borderRadius = '4px';
1523
+ listbox.style.maxHeight = '300px';
1524
+ listbox.style.overflowY = 'auto';
1525
+
1526
+ listbox.addEventListener('mouseup', this.handleOptionClick.bind(this));
1527
+ return listbox;
1528
+ }
1529
+
1530
+ private positionBodyAppend(): void {
1531
+ if (!this.bodyAppendContainer || !this.popup) return;
1532
+
1533
+ const rect = this.getBoundingClientRect();
1534
+
1535
+ // Position the body append container to match the popup position
1536
+ this.bodyAppendContainer.style.left = `${rect.left}px`;
1537
+ this.bodyAppendContainer.style.top = `${rect.bottom + 6}px`; // 6px is the distance from the popup
1538
+ this.bodyAppendContainer.style.width = `${rect.width}px`;
1539
+ this.bodyAppendContainer.style.pointerEvents = 'auto';
1540
+ }
1541
+
1542
+ private updateBodyAppendPosition(): void {
1543
+ if (this.bodyAppend && this.bodyAppendContainer) {
1544
+ this.positionBodyAppend();
1545
+ }
1546
+ }
1547
+
1548
+ private async updateBodyAppendContent(): Promise<void> {
1549
+ if (!this.bodyAppendListbox) return;
1550
+
1551
+ // Clear the body append listbox
1552
+ this.bodyAppendListbox.innerHTML = '';
1553
+
1554
+ // Create a new listbox element
1555
+ const listbox = document.createElement('div');
1556
+ listbox.id = 'listbox';
1557
+ listbox.setAttribute('role', 'listbox');
1558
+ listbox.setAttribute('aria-expanded', this.open ? 'true' : 'false');
1559
+ listbox.setAttribute('aria-multiselectable', this.multiple ? 'true' : 'false');
1560
+ listbox.setAttribute('aria-labelledby', 'label');
1561
+ listbox.setAttribute('part', 'listbox');
1562
+ listbox.className = `select__listbox ${this.searchEnabled ? 'select__search-enabled ' : ''}`;
1563
+ listbox.setAttribute('tabindex', '-1');
1564
+
1565
+ // Render the content using Lit's render function
1566
+ const { render } = await import('lit');
1567
+
1568
+ // Render search
1569
+ if (this.searchEnabled) {
1570
+ const searchContent = this.renderSearch();
1571
+ const searchDiv = document.createElement('div');
1572
+ render(searchContent, searchDiv);
1573
+ listbox.appendChild(searchDiv.firstElementChild as HTMLElement);
1574
+ }
1575
+
1576
+ // Render loader
1577
+ const loaderContent = this.renderLoader();
1578
+ const loaderDiv = document.createElement('div');
1579
+ render(loaderContent, loaderDiv);
1580
+ if (loaderDiv.firstElementChild) {
1581
+ listbox.appendChild(loaderDiv.firstElementChild as HTMLElement);
1582
+ }
1583
+
1584
+ // Render virtualized content
1585
+ const virtualizedContent = this.getVirtualizedContent();
1586
+ const virtualizedDiv = document.createElement('div');
1587
+ render(virtualizedContent, virtualizedDiv);
1588
+ if (virtualizedDiv.firstElementChild) {
1589
+ listbox.appendChild(virtualizedDiv.firstElementChild as HTMLElement);
1590
+ }
1591
+
1592
+ // Render footer
1593
+ if (this.multiple) {
1594
+ const footerContent = this.renderFooter();
1595
+ const footerDiv = document.createElement('div');
1596
+ render(footerContent, footerDiv);
1597
+ if (footerDiv.firstElementChild) {
1598
+ listbox.appendChild(footerDiv.firstElementChild as HTMLElement);
1599
+ }
1600
+ }
1601
+
1602
+ // Append the listbox to the body append container
1603
+ this.bodyAppendListbox.appendChild(listbox);
1604
+
1605
+ // Re-attach event listeners
1606
+ this.attachBodyAppendEventListeners();
1607
+ }
1608
+
1609
+ private attachBodyAppendEventListeners(): void {
1610
+ if (!this.bodyAppendListbox) return;
1611
+
1612
+ // Find the cloned listbox within the body append container
1613
+ const clonedListbox = this.bodyAppendListbox.querySelector('.select__listbox') as HTMLElement;
1614
+ if (!clonedListbox) return;
1615
+
1616
+ // Re-attach the option click handler - use event delegation for better performance
1617
+ clonedListbox.addEventListener('mouseup', this.handleOptionClick.bind(this));
1618
+
1619
+ // Also add click handler for nile-option elements specifically
1620
+ clonedListbox.addEventListener('click', (event) => {
1621
+ const target = event.target as HTMLElement;
1622
+ const option = target.closest('nile-option');
1623
+ if (option) {
1624
+ event.preventDefault();
1625
+ event.stopPropagation();
1626
+ this.handleOptionClick(event);
1627
+ }
1628
+ });
1629
+
1630
+ // Re-attach search handlers if search is enabled
1631
+ const searchInput = clonedListbox.querySelector('nile-input');
1632
+ if (searchInput) {
1633
+ searchInput.addEventListener('nile-input', this.handleSearchChange.bind(this));
1634
+ searchInput.addEventListener('nile-focus', this.handleSearchFocus.bind(this));
1635
+ searchInput.addEventListener('nile-change', this.onInputChange.bind(this));
1636
+ }
1637
+
1638
+ // Re-attach scroll handler
1639
+ const virtualizedContainer = clonedListbox.querySelector('.virtualized') as HTMLElement;
1640
+ if (virtualizedContainer) {
1641
+ virtualizedContainer.addEventListener('scroll', this.handleScroll.bind(this));
1642
+ }
1643
+
1644
+ // Re-attach footer handlers if multiple
1645
+ if (this.multiple) {
1646
+ const showSelectedSpan = clonedListbox.querySelector('.select__footer span[style*="cursor: pointer"]');
1647
+ if (showSelectedSpan) {
1648
+ showSelectedSpan.addEventListener('click', this.toggleShowSelected.bind(this));
1649
+ }
1650
+
1651
+ const clearSpan = clonedListbox.querySelector('.select__clear');
1652
+ if (clearSpan) {
1653
+ clearSpan.addEventListener('click', this.unSelectAll.bind(this));
1654
+ }
1655
+ }
1656
+ }
1657
+
1658
+ private setupBodyAppend(): void {
1659
+ if (!this.bodyAppend) return;
1660
+
1661
+ this.bodyAppendContainer = this.createBodyAppendContainer();
1662
+ this.bodyAppendListbox = this.createBodyAppendListbox();
1663
+
1664
+ this.bodyAppendContainer.appendChild(this.bodyAppendListbox);
1665
+ document.body.appendChild(this.bodyAppendContainer);
1666
+
1667
+ this.positionBodyAppend();
1668
+
1669
+ // Wait for the next tick to ensure the original listbox is rendered
1670
+ this.updateComplete.then(async () => {
1671
+ await this.updateBodyAppendContent();
1672
+ });
1673
+ }
1674
+
1675
+ private cleanupBodyAppend(): void {
1676
+ if (this.bodyAppendContainer && this.bodyAppendContainer.parentNode) {
1677
+ this.bodyAppendContainer.parentNode.removeChild(this.bodyAppendContainer);
1678
+ }
1679
+ this.bodyAppendContainer = null;
1680
+ this.bodyAppendListbox = null;
1681
+ }
1428
1682
  }
1429
1683
 
1430
1684
  setDefaultAnimation('select.show', {