@aquera/nile-elements 1.8.0 → 1.8.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 (51) hide show
  1. package/README.md +3 -0
  2. package/dist/index-6faafdf4.cjs.js +2 -0
  3. package/dist/index-6faafdf4.cjs.js.map +1 -0
  4. package/dist/index-9931b440.esm.js +1 -0
  5. package/dist/index.cjs.js +1 -1
  6. package/dist/index.esm.js +1 -1
  7. package/dist/index.js +283 -283
  8. package/dist/nile-combobox/index.cjs.js +1 -1
  9. package/dist/nile-combobox/index.esm.js +1 -1
  10. package/dist/nile-combobox/nile-combobox.cjs.js +1 -1
  11. package/dist/nile-combobox/nile-combobox.esm.js +1 -1
  12. package/dist/nile-context-menu/nile-context-menu.cjs.js +1 -1
  13. package/dist/nile-context-menu/nile-context-menu.cjs.js.map +1 -1
  14. package/dist/nile-context-menu/nile-context-menu.esm.js +2 -2
  15. package/dist/nile-context-menu-item/nile-context-menu-item.cjs.js +1 -1
  16. package/dist/nile-context-menu-item/nile-context-menu-item.cjs.js.map +1 -1
  17. package/dist/nile-context-menu-item/nile-context-menu-item.esm.js +3 -3
  18. package/dist/nile-context-submenu/nile-context-submenu.cjs.js +1 -1
  19. package/dist/nile-context-submenu/nile-context-submenu.cjs.js.map +1 -1
  20. package/dist/nile-context-submenu/nile-context-submenu.esm.js +2 -2
  21. package/dist/nile-detail/index.cjs.js +1 -1
  22. package/dist/nile-detail/index.esm.js +1 -1
  23. package/dist/nile-detail/nile-detail.cjs.js +1 -1
  24. package/dist/nile-detail/nile-detail.esm.js +1 -1
  25. package/dist/nile-floating-panel/nile-floating-panel.cjs.js +1 -1
  26. package/dist/nile-floating-panel/nile-floating-panel.cjs.js.map +1 -1
  27. package/dist/nile-floating-panel/nile-floating-panel.esm.js +1 -1
  28. package/dist/src/nile-context-menu/nile-context-menu.d.ts +10 -1
  29. package/dist/src/nile-context-menu/nile-context-menu.js +85 -5
  30. package/dist/src/nile-context-menu/nile-context-menu.js.map +1 -1
  31. package/dist/src/nile-context-menu-item/nile-context-menu-item.d.ts +1 -0
  32. package/dist/src/nile-context-menu-item/nile-context-menu-item.js +5 -1
  33. package/dist/src/nile-context-menu-item/nile-context-menu-item.js.map +1 -1
  34. package/dist/src/nile-context-submenu/nile-context-submenu.d.ts +9 -0
  35. package/dist/src/nile-context-submenu/nile-context-submenu.js +100 -16
  36. package/dist/src/nile-context-submenu/nile-context-submenu.js.map +1 -1
  37. package/dist/src/nile-floating-panel/nile-floating-panel.d.ts +2 -0
  38. package/dist/src/nile-floating-panel/nile-floating-panel.js +4 -0
  39. package/dist/src/nile-floating-panel/nile-floating-panel.js.map +1 -1
  40. package/dist/src/version.js +1 -1
  41. package/dist/src/version.js.map +1 -1
  42. package/dist/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +1 -1
  44. package/src/nile-context-menu/nile-context-menu.ts +86 -8
  45. package/src/nile-context-menu-item/nile-context-menu-item.ts +3 -1
  46. package/src/nile-context-submenu/nile-context-submenu.ts +97 -12
  47. package/src/nile-floating-panel/nile-floating-panel.ts +5 -0
  48. package/vscode-html-custom-data.json +13 -3
  49. package/dist/index-644974d4.esm.js +0 -1
  50. package/dist/index-dd2af8ec.cjs.js +0 -2
  51. package/dist/index-dd2af8ec.cjs.js.map +0 -1
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Webcomponent nile-elements following open-wc recommendations",
4
4
  "license": "MIT",
5
5
  "author": "nile-elements",
6
- "version": "1.8.0",
6
+ "version": "1.8.1",
7
7
  "main": "dist/src/index.js",
8
8
  "type": "module",
9
9
  "module": "dist/src/index.js",
@@ -30,6 +30,7 @@ export interface NileContextMenuItemData {
30
30
  iconMethod?: 'fill' | 'stroke' | string;
31
31
  iconColor?: string;
32
32
  submenu?: NileContextMenuData[];
33
+ open?: boolean;
33
34
  }
34
35
 
35
36
  export interface NileContextMenuGroupData {
@@ -93,7 +94,9 @@ export class NileContextMenu extends NileElement {
93
94
  @property({ attribute: true, type: String, reflect: true }) skipOn = '';
94
95
 
95
96
  @property({ attribute: true, type: Number, reflect: true }) zIndex = 9999;
96
-
97
+
98
+ @property({ attribute: true, type: Boolean, reflect: true }) open = false;
99
+
97
100
  @property({ attribute: false }) public items: NileContextMenuData[] = [];
98
101
 
99
102
  private get _isDataMode(): boolean {
@@ -102,9 +105,11 @@ export class NileContextMenu extends NileElement {
102
105
  @query('nile-floating-panel') private _floatingPanel?: HTMLElement;
103
106
 
104
107
  @state() private _items: NileContextMenuItem[] = [];
105
-
108
+
106
109
  @state() private _open = false;
107
-
110
+
111
+ private _pinned = false;
112
+
108
113
  protected _openContext: NileContextMenuOpenContext | null = null;
109
114
 
110
115
  protected _targetEl: Element | null = null;
@@ -132,6 +137,15 @@ export class NileContextMenu extends NileElement {
132
137
  this._relocateLightChildren();
133
138
  });
134
139
  this._lightObserver.observe(this, { childList: true });
140
+ // Restore a declaratively-opened menu after a disconnect/reconnect.
141
+ if (this.open && !this._open) {
142
+ this.updateComplete.then(() => {
143
+ if (this.isConnected && this.open && !this._open) {
144
+ this._pinned = true;
145
+ this._openAtTarget();
146
+ }
147
+ });
148
+ }
135
149
  }
136
150
 
137
151
  protected override updated(changed: PropertyValues): void {
@@ -154,8 +168,33 @@ export class NileContextMenu extends NileElement {
154
168
  this._wasDataMode = false;
155
169
  }
156
170
  }
171
+ if (changed.has('open')) {
172
+ if (this.open && !this._open) {
173
+ this._pinned = true;
174
+ this._openAtTarget();
175
+ } else if (!this.open && this._open) {
176
+ this.close('programmatic');
177
+ }
178
+ }
157
179
  }
158
180
 
181
+ /** Open anchored to the `for` target (used when the `open` property is set). */
182
+ private _openAtTarget(): void {
183
+ const rect = this._targetEl?.getBoundingClientRect();
184
+ this.openAt({
185
+ x: rect ? rect.left : 0,
186
+ y: rect ? rect.bottom : 0,
187
+ target: this._targetEl ?? undefined,
188
+ });
189
+ }
190
+
191
+ /** Re-anchor a pinned menu to its target after layout changes. */
192
+ private _onPinnedReposition = (): void => {
193
+ if (!this._open || !this._pinned || !this._targetEl) return;
194
+ const rect = this._targetEl.getBoundingClientRect();
195
+ this._positionProxyAt(rect.left, rect.bottom);
196
+ };
197
+
159
198
  private _resolveTarget(): void {
160
199
  const value = this.for?.trim();
161
200
  if (!value) {
@@ -196,7 +235,7 @@ export class NileContextMenu extends NileElement {
196
235
  if (isInteractive(me.target as Element | null)) return;
197
236
  me.preventDefault();
198
237
  if (this._open) return;
199
- this.open({
238
+ this.openAt({
200
239
  x: me.clientX,
201
240
  y: me.clientY,
202
241
  target: (me.target as Element | null) ?? undefined,
@@ -219,7 +258,7 @@ export class NileContextMenu extends NileElement {
219
258
  const handler = (e: Event) => {
220
259
  const me = e as MouseEvent;
221
260
  me.preventDefault();
222
- this.open({
261
+ this.openAt({
223
262
  x: me.clientX,
224
263
  y: me.clientY,
225
264
  target,
@@ -233,7 +272,7 @@ export class NileContextMenu extends NileElement {
233
272
  if (wantsClick) {
234
273
  const handler = (e: Event) => {
235
274
  const me = e as MouseEvent;
236
- this.open({
275
+ this.openAt({
237
276
  x: me.clientX,
238
277
  y: me.clientY,
239
278
  target,
@@ -267,6 +306,7 @@ export class NileContextMenu extends NileElement {
267
306
  document.removeEventListener('pointerdown', this._onOutsidePointer, true);
268
307
  document.removeEventListener('keydown', this._onKeydown, true);
269
308
  window.removeEventListener('scroll', this._onScroll, true);
309
+ window.removeEventListener('resize', this._onPinnedReposition);
270
310
  this._proxyEl?.remove();
271
311
  this._proxyEl = undefined;
272
312
  }
@@ -312,8 +352,21 @@ export class NileContextMenu extends NileElement {
312
352
 
313
353
  private _positionProxyAt(x: number, y: number): void {
314
354
  if (!this._proxyEl) return;
355
+ if (this._pinned && this._targetEl) {
356
+ // menu attaches below it pinned menus (or flips cleanly above it)
357
+ const rect = this._targetEl.getBoundingClientRect();
358
+ this._proxyEl.style.position = 'absolute';
359
+ this._proxyEl.style.left = `${rect.left + window.scrollX}px`;
360
+ this._proxyEl.style.top = `${rect.top + window.scrollY}px`;
361
+ this._proxyEl.style.width = `${rect.width}px`;
362
+ this._proxyEl.style.height = `${rect.height}px`;
363
+ return;
364
+ }
365
+ this._proxyEl.style.position = 'fixed';
315
366
  this._proxyEl.style.left = `${x}px`;
316
367
  this._proxyEl.style.top = `${y}px`;
368
+ this._proxyEl.style.width = '0';
369
+ this._proxyEl.style.height = '0';
317
370
  }
318
371
 
319
372
  private _relocateLightChildren(): void {
@@ -352,6 +405,7 @@ export class NileContextMenu extends NileElement {
352
405
  if (data.id) item.id = data.id;
353
406
  item.value = data.value ?? data.id ?? '';
354
407
  if (data.disabled) item.disabled = true;
408
+ if (data.open) item.open = true;
355
409
  if (data.icon) {
356
410
  const glyph = document.createElement('nile-glyph');
357
411
  glyph.setAttribute('slot', 'icon');
@@ -401,7 +455,7 @@ export class NileContextMenu extends NileElement {
401
455
  return this._open;
402
456
  }
403
457
 
404
- public open(options: NileContextMenuOpenOptions): void {
458
+ public openAt(options: NileContextMenuOpenOptions): void {
405
459
  if (this._open) return;
406
460
  for (const other of NileContextMenu._openInstances) {
407
461
  if (other !== this) other.close('programmatic');
@@ -416,11 +470,14 @@ export class NileContextMenu extends NileElement {
416
470
  this._ensureProxy();
417
471
  this._positionProxyAt(options.x, options.y);
418
472
  this._open = true;
473
+ this.open = true;
419
474
  NileContextMenu._openInstances.add(this);
420
475
  document.addEventListener('pointerdown', this._onOutsidePointer, true);
421
476
  document.addEventListener('keydown', this._onKeydown, true);
477
+ if (this._pinned) window.addEventListener('resize', this._onPinnedReposition);
422
478
  requestAnimationFrame(() => {
423
479
  if (this._open) window.addEventListener('scroll', this._onScroll, true);
480
+ requestAnimationFrame(() => this._openPinnedSubmenus());
424
481
  });
425
482
  this.emit('nile-change', { type: 'open', ...this._openContext });
426
483
  }
@@ -428,6 +485,8 @@ export class NileContextMenu extends NileElement {
428
485
  public close(reason: NileContextMenuCloseReason = 'programmatic'): void {
429
486
  if (!this._open) return;
430
487
  this._open = false;
488
+ this.open = false;
489
+ this._pinned = false;
431
490
  this._openContext = null;
432
491
  NileContextMenu._openInstances.delete(this);
433
492
  const subs = this._menuContainerRef?.querySelectorAll(SUBMENU_TAG);
@@ -435,6 +494,7 @@ export class NileContextMenu extends NileElement {
435
494
  document.removeEventListener('pointerdown', this._onOutsidePointer, true);
436
495
  document.removeEventListener('keydown', this._onKeydown, true);
437
496
  window.removeEventListener('scroll', this._onScroll, true);
497
+ window.removeEventListener('resize', this._onPinnedReposition);
438
498
  const active = document.activeElement;
439
499
  const focusInMenu =
440
500
  active === this ||
@@ -533,7 +593,8 @@ export class NileContextMenu extends NileElement {
533
593
  const sub = this._findSubmenuOwning(container);
534
594
  if (!sub) return;
535
595
  e.preventDefault();
536
- sub.closeSubmenu?.();
596
+ // Pinned submenus (parent item has `open`) stay open; just move focus.
597
+ if (!sub.parentItem?.open) sub.closeSubmenu?.();
537
598
  sub.parentItem?.focus();
538
599
  return;
539
600
  }
@@ -578,6 +639,10 @@ export class NileContextMenu extends NileElement {
578
639
 
579
640
  private _onScroll = (e: Event): void => {
580
641
  if (!this._open) return;
642
+ if (this._pinned) {
643
+ this._onPinnedReposition();
644
+ return;
645
+ }
581
646
  const path = e.composedPath();
582
647
  // Scroll inside the root menu container?
583
648
  if (this._menuContainerRef && path.includes(this._menuContainerRef)) return;
@@ -615,8 +680,21 @@ export class NileContextMenu extends NileElement {
615
680
 
616
681
  private _onPanelShown = (e: Event): void => {
617
682
  e.stopPropagation();
683
+ this._openPinnedSubmenus();
618
684
  };
619
685
 
686
+ /** Open the submenu of every top-level item carrying the `open` attribute. */
687
+ private _openPinnedSubmenus(): void {
688
+ if (!this._open || !this._menuContainerRef) return;
689
+ for (const item of this._items) {
690
+ if (!item.open || item.disabled) continue;
691
+ const sub = item.querySelector(`:scope > ${SUBMENU_TAG}`) as
692
+ | (HTMLElement & { openSubmenu?: () => void })
693
+ | null;
694
+ sub?.openSubmenu?.();
695
+ }
696
+ }
697
+
620
698
  private _onMenuMouseOver = (e: MouseEvent): void => {
621
699
  const item = e
622
700
  .composedPath()
@@ -41,6 +41,8 @@ export class NileContextMenuItem extends NileElement {
41
41
 
42
42
  @property({ attribute: true, type: Boolean, reflect: true }) disabled = false;
43
43
 
44
+ @property({ attribute: true, type: Boolean, reflect: true }) open = false;
45
+
44
46
  public onSelect?: NileContextMenuItemSelectHandler;
45
47
 
46
48
  @state() private _hasSubmenu = false;
@@ -95,7 +97,7 @@ export class NileContextMenuItem extends NileElement {
95
97
  <span part="label" class="label"><slot @slotchange=${this._onSlotChange}></slot></span>
96
98
  ${this._hasSubmenu
97
99
  ? html`<span part="chevron" class="chevron" aria-hidden="true">
98
- <nile-glyph name="ng-chevron-right" size="16"></nile-glyph>
100
+ <nile-glyph name="ng-chevron-right" size="16" method="stroke"></nile-glyph>
99
101
  </span>`
100
102
  : ''}
101
103
  </div>
@@ -47,6 +47,11 @@ export class NileContextSubmenu extends NileElement {
47
47
  private _openTimer?: number;
48
48
  private _closeTimer?: number;
49
49
  private _setupDone = false;
50
+ private _parentObserver?: MutationObserver;
51
+ private _repositionQueued = false;
52
+ private get _pinnedOpen(): boolean {
53
+ return !!(this._parentItem?.open && !this._parentItem.disabled);
54
+ }
50
55
 
51
56
  public override connectedCallback(): void {
52
57
  super.connectedCallback();
@@ -59,10 +64,27 @@ export class NileContextSubmenu extends NileElement {
59
64
  this._parentItem.addEventListener('mouseenter', this._onParentEnter);
60
65
  this._parentItem.addEventListener('mouseleave', this._onParentLeave);
61
66
  this._parentItem.addEventListener('click', this._onParentClick, true);
67
+ // Open/close the submenu when the parent item's `open` attribute changes.
68
+ this._parentObserver = new MutationObserver(() => {
69
+ if (this._pinnedOpen) {
70
+ if (this._isParentVisible()) this.openSubmenu();
71
+ } else if (this._open) {
72
+ this.closeSubmenu();
73
+ }
74
+ });
75
+ this._parentObserver.observe(this._parentItem, {
76
+ attributes: true,
77
+ attributeFilter: ['open'],
78
+ });
62
79
  }
63
80
  this._ensureBodyPanel();
64
81
  }
65
82
 
83
+ private _isParentVisible(): boolean {
84
+ const rect = this._parentItem?.getBoundingClientRect();
85
+ return !!rect && rect.width > 0 && rect.height > 0;
86
+ }
87
+
66
88
  private _ensureProxy(): void {
67
89
  if (this._proxyEl) return;
68
90
  const el = document.createElement('div');
@@ -77,8 +99,16 @@ export class NileContextSubmenu extends NileElement {
77
99
  private _syncProxyToParent(): void {
78
100
  if (!this._proxyEl || !this._parentItem) return;
79
101
  const rect = this._parentItem.getBoundingClientRect();
80
- this._proxyEl.style.left = `${rect.left}px`;
81
- this._proxyEl.style.top = `${rect.top}px`;
102
+ if (this._pinnedOpen) {
103
+ // Pinned submenus anchor in document coordinates.
104
+ this._proxyEl.style.position = 'absolute';
105
+ this._proxyEl.style.left = `${rect.left + window.scrollX}px`;
106
+ this._proxyEl.style.top = `${rect.top + window.scrollY}px`;
107
+ } else {
108
+ this._proxyEl.style.position = 'fixed';
109
+ this._proxyEl.style.left = `${rect.left}px`;
110
+ this._proxyEl.style.top = `${rect.top}px`;
111
+ }
82
112
  this._proxyEl.style.width = `${rect.width}px`;
83
113
  this._proxyEl.style.height = `${rect.height}px`;
84
114
  }
@@ -90,6 +120,8 @@ export class NileContextSubmenu extends NileElement {
90
120
  this._parentItem.removeEventListener('mouseleave', this._onParentLeave);
91
121
  this._parentItem.removeEventListener('click', this._onParentClick, true);
92
122
  }
123
+ this._parentObserver?.disconnect();
124
+ this._parentObserver = undefined;
93
125
  this._clearTimers();
94
126
  if (this._open) this.closeSubmenu();
95
127
  this._teardownBodyArtifacts();
@@ -172,6 +204,7 @@ export class NileContextSubmenu extends NileElement {
172
204
 
173
205
  private _onParentLeave = (): void => {
174
206
  if (this._openTimer != null) { clearTimeout(this._openTimer); this._openTimer = undefined; }
207
+ if (this._pinnedOpen) return;
175
208
  if (this._open) {
176
209
  this._closeTimer = window.setTimeout(() => this.closeSubmenu(), HOVER_CLOSE_DELAY);
177
210
  }
@@ -181,15 +214,31 @@ export class NileContextSubmenu extends NileElement {
181
214
  e.stopPropagation();
182
215
  if (this._parentItem?.disabled) return;
183
216
  this._clearTimers();
184
- if (this._open) this.closeSubmenu();
185
- else this.openSubmenu();
217
+ if (this._open) {
218
+ if (!this._pinnedOpen) this.closeSubmenu();
219
+ } else {
220
+ this.openSubmenu();
221
+ }
186
222
  };
187
223
 
188
224
  private _onPanelEnter = (): void => {
189
225
  this._clearTimers();
190
226
  };
191
227
 
228
+ //Re-anchor to the parent item after scroll/resize
229
+ private _onReposition = (): void => {
230
+ if (this._repositionQueued || !this._open) return;
231
+ this._repositionQueued = true;
232
+ requestAnimationFrame(() => {
233
+ this._repositionQueued = false;
234
+ if (!this._open) return;
235
+ this._syncProxyToParent();
236
+ this._floatingPanelEl?.reposition();
237
+ });
238
+ };
239
+
192
240
  private _onPanelLeave = (): void => {
241
+ if (this._pinnedOpen) return;
193
242
  if (this._open) {
194
243
  this._closeTimer = window.setTimeout(() => {
195
244
  const hasOpenDescendant = NileContextSubmenu.openStack.some(
@@ -209,11 +258,50 @@ export class NileContextSubmenu extends NileElement {
209
258
  this._ensureBodyPanel();
210
259
  this._syncProxyToParent();
211
260
  this._open = true;
212
- if (this._floatingPanelEl) this._floatingPanelEl.open = true;
261
+ if (this._floatingPanelEl) {
262
+ // open pinned descendants only once this panel is fully shown
263
+ this._floatingPanelEl.addEventListener(
264
+ 'nile-after-show',
265
+ () => {
266
+ if (this._open) this._openPinnedDescendants();
267
+ },
268
+ { once: true },
269
+ );
270
+ this._floatingPanelEl.open = true;
271
+ }
213
272
  NileContextSubmenu.openStack.push(this);
273
+ window.addEventListener('scroll', this._onReposition, true);
274
+ window.addEventListener('resize', this._onReposition);
275
+ requestAnimationFrame(() => requestAnimationFrame(() => {
276
+ if (this._open) this._openPinnedDescendants();
277
+ }));
214
278
  this._parentItem?.setSubmenuExpanded?.(true);
215
279
  }
216
280
 
281
+ /** Open the submenu of every direct child item carrying the `open` attribute. */
282
+ private _openPinnedDescendants(): void {
283
+ if (!this._menuContainerRef) return;
284
+ const items = Array.from(
285
+ this._menuContainerRef.querySelectorAll(ITEM_TAG)
286
+ ) as NileContextMenuItem[];
287
+ for (const item of items) {
288
+ if (!this._isDirectChildItem(item)) continue;
289
+ if (!item.open || item.disabled) continue;
290
+ const sub = item.querySelector(`:scope > ${SUBMENU_TAG}`) as NileContextSubmenu | null;
291
+ sub?.openSubmenu();
292
+ }
293
+ }
294
+
295
+ /** True if `item` sits at this submenu's own level (not inside a deeper submenu). */
296
+ private _isDirectChildItem(item: Element): boolean {
297
+ let cur: Element | null = item.parentElement;
298
+ while (cur && cur !== this._menuContainerRef) {
299
+ if (cur.tagName.toLowerCase() === SUBMENU_TAG) return false;
300
+ cur = cur.parentElement;
301
+ }
302
+ return true;
303
+ }
304
+
217
305
  public closeSubmenu(): void {
218
306
  if (!this._open) return;
219
307
  for (const sub of [...NileContextSubmenu.openStack]) {
@@ -223,6 +311,8 @@ export class NileContextSubmenu extends NileElement {
223
311
  }
224
312
  this._open = false;
225
313
  if (this._floatingPanelEl) this._floatingPanelEl.open = false;
314
+ window.removeEventListener('scroll', this._onReposition, true);
315
+ window.removeEventListener('resize', this._onReposition);
226
316
  NileContextSubmenu.openStack = NileContextSubmenu.openStack.filter(s => s !== this);
227
317
  this._clearTimers();
228
318
  this._parentItem?.setSubmenuExpanded?.(false);
@@ -248,13 +338,7 @@ export class NileContextSubmenu extends NileElement {
248
338
  this._menuContainerRef.querySelectorAll(ITEM_TAG)
249
339
  ) as NileContextMenuItem[];
250
340
  for (const item of items) {
251
- let cur: Element | null = item.parentElement;
252
- let inDeeperSub = false;
253
- while (cur && cur !== this._menuContainerRef) {
254
- if (cur.tagName.toLowerCase() === SUBMENU_TAG) { inDeeperSub = true; break; }
255
- cur = cur.parentElement;
256
- }
257
- if (inDeeperSub) continue;
341
+ if (!this._isDirectChildItem(item)) continue;
258
342
  if (!item.disabled) { item.focus(); return; }
259
343
  }
260
344
  }
@@ -263,6 +347,7 @@ export class NileContextSubmenu extends NileElement {
263
347
  const myLevel = this._parentLevel();
264
348
  for (const sub of [...NileContextSubmenu.openStack]) {
265
349
  if (sub === this) continue;
350
+ if (sub._pinnedOpen) continue;
266
351
  if (sub._parentLevel() === myLevel) sub.closeSubmenu();
267
352
  }
268
353
  }
@@ -303,6 +303,11 @@ export class NileFloatingPanel extends NileElement {
303
303
  this._attachTippy();
304
304
  }
305
305
 
306
+ /** Recomputes the panel position against its anchor (e.g. after the anchor moved). */
307
+ public reposition(): void {
308
+ this.tippyInstance?.popperInstance?.update();
309
+ }
310
+
306
311
  /** Returns the current resolved placement from Tippy/Popper. */
307
312
  public getCurrentPlacement(): string {
308
313
  const popper = this.tippyInstance?.popper;
@@ -1712,7 +1712,7 @@
1712
1712
  },
1713
1713
  {
1714
1714
  "name": "nile-context-menu-item",
1715
- "description": "Nile context-menu item. A clickable entry inside `nile-context-menu`.\n\nSlots:\n\n * ` ` {} - Default slot for the visible label.\n\n * `icon` {} - Optional leading icon (e.g. a `<nile-glyph slot=\"icon\">`).\n\nAttributes:\n\n * `value` {`string`} - \n\n * `disabled` {`boolean`} - \n\nProperties:\n\n * `value` {`string`} - \n\n * `disabled` {`boolean`} - \n\n * `onSelect` - \n\n * `_hasSubmenu` {`boolean`} - \n\n * `_hasIcon` {`boolean`} - \n\n * `_submenuExpanded` {`boolean`} - \n\n * `override` - \n\n * `hasSubmenu` {`boolean`} - \n\n * `_onSlotChange` - \n\n * `_onIconSlotChange` - \n\n * `BUBBLES` {`boolean`} - \n\n * `COMPOSED` {`boolean`} - \n\n * `CANCELABLE` {`boolean`} - ",
1715
+ "description": "Nile context-menu item. A clickable entry inside `nile-context-menu`.\n\nSlots:\n\n * ` ` {} - Default slot for the visible label.\n\n * `icon` {} - Optional leading icon (e.g. a `<nile-glyph slot=\"icon\">`).\n\nAttributes:\n\n * `value` {`string`} - \n\n * `disabled` {`boolean`} - \n\n * `open` {`boolean`} - \n\nProperties:\n\n * `value` {`string`} - \n\n * `disabled` {`boolean`} - \n\n * `open` {`boolean`} - \n\n * `onSelect` - \n\n * `_hasSubmenu` {`boolean`} - \n\n * `_hasIcon` {`boolean`} - \n\n * `_submenuExpanded` {`boolean`} - \n\n * `override` - \n\n * `hasSubmenu` {`boolean`} - \n\n * `_onSlotChange` - \n\n * `_onIconSlotChange` - \n\n * `BUBBLES` {`boolean`} - \n\n * `COMPOSED` {`boolean`} - \n\n * `CANCELABLE` {`boolean`} - ",
1716
1716
  "attributes": [
1717
1717
  {
1718
1718
  "name": "value",
@@ -1722,12 +1722,17 @@
1722
1722
  "name": "disabled",
1723
1723
  "description": "`disabled` {`boolean`} - \n\nProperty: disabled\n\nDefault: false",
1724
1724
  "valueSet": "v"
1725
+ },
1726
+ {
1727
+ "name": "open",
1728
+ "description": "`open` {`boolean`} - \n\nProperty: open\n\nDefault: false",
1729
+ "valueSet": "v"
1725
1730
  }
1726
1731
  ]
1727
1732
  },
1728
1733
  {
1729
1734
  "name": "nile-context-menu",
1730
- "description": "Nile context-menu component.\n\nEvents:\n\n * `nile` {} - change Fired on menu lifecycle and selection. `detail.type` is one of\n`'open'`, `'click'`, or `'close'`; remaining fields depend on the type.\n\nSlots:\n\n * ` ` {} - Default menu content.\n\nAttributes:\n\n * `for` {`string`} - \n\n * `trigger` {`NileContextMenuTrigger`} - \n\n * `skipOn` {`string`} - \n\n * `zIndex` {`number`} - \n\nProperties:\n\n * `for` {`string`} - \n\n * `trigger` {`NileContextMenuTrigger`} - \n\n * `skipOn` {`string`} - \n\n * `zIndex` {`number`} - \n\n * `items` {`NileContextMenuGroupData[]`} - \n\n * `_isDataMode` {`boolean`} - \n\n * `_floatingPanel` {`HTMLElement | undefined`} - \n\n * `_items` {`NileContextMenuItem[]`} - \n\n * `_open` {`boolean`} - \n\n * `_openContext` {`NileContextMenuOpenContext | null`} - \n\n * `_targetEl` {`Element | null`} - \n\n * `_previouslyFocused` {`HTMLElement | null`} - \n\n * `_openInstances` {`Set<NileContextMenu>`} - \n\n * `_detachTriggers` - \n\n * `_lightObserver` {`MutationObserver | undefined`} - \n\n * `_menuObserver` {`MutationObserver | undefined`} - \n\n * `_proxyEl` {`HTMLDivElement | undefined`} - \n\n * `_proxyId` {`string`} - \n\n * `_wasDataMode` {`boolean`} - \n\n * `targetElement` {`Element | null`} - \n\n * `_menuContainerRef` {`HTMLDivElement | null`} - \n\n * `override` - \n\n * `menuItems` {`readonly NileContextMenuItem[]`} - \n\n * `isOpen` {`boolean`} - \n\n * `_onKeydown` - \n\n * `_onScroll` - \n\n * `_onOutsidePointer` - \n\n * `_onDescendantSelect` - \n\n * `_onPanelShown` - \n\n * `_onMenuMouseOver` - \n\n * `_onMenuClick` - \n\n * `BUBBLES` {`boolean`} - \n\n * `COMPOSED` {`boolean`} - \n\n * `CANCELABLE` {`boolean`} - ",
1735
+ "description": "Nile context-menu component.\n\nEvents:\n\n * `nile` {} - change Fired on menu lifecycle and selection. `detail.type` is one of\n`'open'`, `'click'`, or `'close'`; remaining fields depend on the type.\n\nSlots:\n\n * ` ` {} - Default menu content.\n\nAttributes:\n\n * `for` {`string`} - \n\n * `trigger` {`NileContextMenuTrigger`} - \n\n * `skipOn` {`string`} - \n\n * `zIndex` {`number`} - \n\n * `open` {`boolean`} - \n\nProperties:\n\n * `for` {`string`} - \n\n * `trigger` {`NileContextMenuTrigger`} - \n\n * `skipOn` {`string`} - \n\n * `zIndex` {`number`} - \n\n * `open` {`boolean`} - \n\n * `items` {`NileContextMenuGroupData[]`} - \n\n * `_isDataMode` {`boolean`} - \n\n * `_floatingPanel` {`HTMLElement | undefined`} - \n\n * `_items` {`NileContextMenuItem[]`} - \n\n * `_open` {`boolean`} - \n\n * `_pinned` {`boolean`} - \n\n * `_openContext` {`NileContextMenuOpenContext | null`} - \n\n * `_targetEl` {`Element | null`} - \n\n * `_previouslyFocused` {`HTMLElement | null`} - \n\n * `_openInstances` {`Set<NileContextMenu>`} - \n\n * `_detachTriggers` - \n\n * `_lightObserver` {`MutationObserver | undefined`} - \n\n * `_menuObserver` {`MutationObserver | undefined`} - \n\n * `_proxyEl` {`HTMLDivElement | undefined`} - \n\n * `_proxyId` {`string`} - \n\n * `_wasDataMode` {`boolean`} - \n\n * `_onPinnedReposition` - Re-anchor a pinned menu to its target after layout changes.\n\n * `targetElement` {`Element | null`} - \n\n * `_menuContainerRef` {`HTMLDivElement | null`} - \n\n * `override` - \n\n * `menuItems` {`readonly NileContextMenuItem[]`} - \n\n * `isOpen` {`boolean`} - \n\n * `_onKeydown` - \n\n * `_onScroll` - \n\n * `_onOutsidePointer` - \n\n * `_onDescendantSelect` - \n\n * `_onPanelShown` - \n\n * `_onMenuMouseOver` - \n\n * `_onMenuClick` - \n\n * `BUBBLES` {`boolean`} - \n\n * `COMPOSED` {`boolean`} - \n\n * `CANCELABLE` {`boolean`} - ",
1731
1736
  "attributes": [
1732
1737
  {
1733
1738
  "name": "for",
@@ -1762,6 +1767,11 @@
1762
1767
  "name": "zIndex",
1763
1768
  "description": "`zIndex` {`number`} - \n\nProperty: zIndex\n\nDefault: 9999"
1764
1769
  },
1770
+ {
1771
+ "name": "open",
1772
+ "description": "`open` {`boolean`} - \n\nProperty: open\n\nDefault: false",
1773
+ "valueSet": "v"
1774
+ },
1765
1775
  {
1766
1776
  "name": "onnile",
1767
1777
  "description": "`nile` {} - change Fired on menu lifecycle and selection. `detail.type` is one of\n`'open'`, `'click'`, or `'close'`; remaining fields depend on the type."
@@ -1770,7 +1780,7 @@
1770
1780
  },
1771
1781
  {
1772
1782
  "name": "nile-context-submenu",
1773
- "description": "Nested submenu inside a `nile-context-menu-item`.\n\nAttributes:\n\n * `zIndex` {`number`} - \n\nProperties:\n\n * `zIndex` {`number`} - \n\n * `_open` {`boolean`} - \n\n * `openStack` {`NileContextSubmenu[]`} - \n\n * `_parentItem` - \n\n * `_proxyId` {`string`} - \n\n * `_proxyEl` {`HTMLDivElement | undefined`} - \n\n * `_floatingPanelEl` - \n\n * `_menuContainerRef` {`HTMLDivElement | null`} - \n\n * `_openTimer` {`number | undefined`} - \n\n * `_closeTimer` {`number | undefined`} - \n\n * `_setupDone` {`boolean`} - \n\n * `override` - \n\n * `_onParentEnter` - \n\n * `_onParentLeave` - \n\n * `_onParentClick` - \n\n * `_onPanelEnter` - \n\n * `_onPanelLeave` - \n\n * `isOpen` {`boolean`} - \n\n * `parentItem` - \n\n * `_onMenuMouseOver` - \n\n * `_onNestedSelect` - \n\n * `_onMenuClick` - \n\n * `BUBBLES` {`boolean`} - \n\n * `COMPOSED` {`boolean`} - \n\n * `CANCELABLE` {`boolean`} - ",
1783
+ "description": "Nested submenu inside a `nile-context-menu-item`.\n\nAttributes:\n\n * `zIndex` {`number`} - \n\nProperties:\n\n * `zIndex` {`number`} - \n\n * `_open` {`boolean`} - \n\n * `openStack` {`NileContextSubmenu[]`} - \n\n * `_parentItem` - \n\n * `_proxyId` {`string`} - \n\n * `_proxyEl` {`HTMLDivElement | undefined`} - \n\n * `_floatingPanelEl` - \n\n * `_menuContainerRef` {`HTMLDivElement | null`} - \n\n * `_openTimer` {`number | undefined`} - \n\n * `_closeTimer` {`number | undefined`} - \n\n * `_setupDone` {`boolean`} - \n\n * `_parentObserver` {`MutationObserver | undefined`} - \n\n * `_repositionQueued` {`boolean`} - \n\n * `_pinnedOpen` {`boolean`} - \n\n * `override` - \n\n * `_onParentEnter` - \n\n * `_onParentLeave` - \n\n * `_onParentClick` - \n\n * `_onPanelEnter` - \n\n * `_onReposition` - \n\n * `_onPanelLeave` - \n\n * `isOpen` {`boolean`} - \n\n * `parentItem` - \n\n * `_onMenuMouseOver` - \n\n * `_onNestedSelect` - \n\n * `_onMenuClick` - \n\n * `BUBBLES` {`boolean`} - \n\n * `COMPOSED` {`boolean`} - \n\n * `CANCELABLE` {`boolean`} - ",
1774
1784
  "attributes": [
1775
1785
  {
1776
1786
  "name": "zIndex",
@@ -1 +0,0 @@
1
- function t(t,s,i){let h,n=i.initialDeps??[],e=!0;function l(){const l=t();return l.length!==n.length||l.some(((t,s)=>n[s]!==t))?(n=l,h=s(...l),!(null==i?void 0:i.onChange)||e&&i.skipInitialOnChange||i.onChange(h),e=!1,h):h}return l.updateDeps=t=>{n=t},l}function s(t,s){if(void 0===t)throw new Error("Unexpected undefined"+(s?`: ${s}`:""));return t}const i=(t,s)=>Math.abs(t-s)<1.01,h=(t,s,i)=>{let h;return function(...n){t.clearTimeout(h),h=t.setTimeout((()=>s.apply(this,n)),i)}};let n;const e=()=>{if(void 0!==n)return n;if("undefined"==typeof navigator)return n=!1;if(/iP(hone|od|ad)/.test(navigator.userAgent))return n=!0;const t=navigator.maxTouchPoints;return n="MacIntel"===navigator.platform&&void 0!==t&&t>0},l=t=>{const{offsetWidth:s,offsetHeight:i}=t;return{width:s,height:i}},r=t=>t,o=t=>{const s=Math.max(t.startIndex-t.overscan,0),i=Math.min(t.endIndex+t.overscan,t.count-1)-s+1,h=new Array(i);for(let t=0;t<i;t++)h[t]=s+t;return h},u=(t,s)=>{const i=t.scrollElement;if(!i)return;const h=t.targetWindow;if(!h)return;const n=t=>{const{width:i,height:h}=t;s({width:Math.round(i),height:Math.round(h)})};if(n(l(i)),!h.ResizeObserver)return()=>{};const e=new h.ResizeObserver((s=>{const h=()=>{const t=s[0];if(null==t?void 0:t.borderBoxSize){const s=t.borderBoxSize[0];if(s)return void n({width:s.inlineSize,height:s.blockSize})}n(l(i))};t.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(h):h()}));return e.observe(i,{box:"border-box"}),()=>{e.unobserve(i)}},a={passive:!0},c="undefined"==typeof window||"onscrollend"in window,d=(t,s)=>((t,s,i)=>{const n=t.scrollElement;if(!n)return;const e=t.targetWindow;if(!e)return;const l=t.options.useScrollendEvent&&c;let r=0;const o=l?null:h(e,(()=>s(r,!1)),t.options.isScrollingResetDelay),u=t=>()=>{r=i(n),null==o||o(),s(r,t)},d=u(!0),f=u(!1);return n.addEventListener("scroll",d,a),l&&n.addEventListener("scrollend",f,a),()=>{n.removeEventListener("scroll",d),l&&n.removeEventListener("scrollend",f)}})(t,s,(s=>{const{horizontal:i,isRtl:h}=t.options;return i?s.scrollLeft*(h?-1:1):s.scrollTop})),f=(t,s,i)=>{if(null==s?void 0:s.borderBoxSize){const t=s.borderBoxSize[0];if(t){return Math.round(t[i.options.horizontal?"inlineSize":"blockSize"])}}return t[i.options.horizontal?"offsetWidth":"offsetHeight"]},v=(t,{adjustments:s=0,behavior:i},h)=>{var n,e;null==(e=null==(n=h.scrollElement)?void 0:n.scrollTo)||e.call(n,{[h.options.horizontal?"left":"top"]:t+s,behavior:i})};class b{constructor(h){this.unsubs=[],this.scrollElement=null,this.targetWindow=null,this.isScrolling=!1,this.scrollState=null,this.measurementsCache=[],this._flatMeasurements=null,this.itemSizeCache=new Map,this.itemSizeCacheVersion=0,this.laneAssignments=new Map,this.pendingMin=null,this.prevLanes=void 0,this.lanesChangedFlag=!1,this.lanesSettling=!1,this.pendingScrollAnchor=null,this.scrollRect=null,this.scrollOffset=null,this.scrollDirection=null,this.scrollAdjustments=0,this._iosDeferredAdjustment=0,this._iosTouching=!1,this._iosJustTouchEnded=!1,this._iosTouchEndTimerId=null,this._intendedScrollOffset=null,this.elementsCache=new Map,this.now=()=>{var t,s,i;return(null==(i=null==(s=null==(t=this.targetWindow)?void 0:t.performance)?void 0:s.now)?void 0:i.call(s))??Date.now()},this.observer=(()=>{let t=null;const s=()=>t||(this.targetWindow&&this.targetWindow.ResizeObserver?t=new this.targetWindow.ResizeObserver((t=>{t.forEach((t=>{const s=()=>{const s=t.target,i=this.indexFromElement(s);if(s.isConnected)this.shouldMeasureDuringScroll(i)&&this.resizeItem(i,this.options.measureElement(s,t,this));else{this.observer.unobserve(s);for(const[t,i]of this.elementsCache)if(i===s){this.elementsCache.delete(t);break}}};this.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(s):s()}))})):null);return{disconnect:()=>{var i;null==(i=s())||i.disconnect(),t=null},observe:t=>{var i;return null==(i=s())?void 0:i.observe(t,{box:"border-box"})},unobserve:t=>{var i;return null==(i=s())?void 0:i.unobserve(t)}}})(),this.range=null,this.setOptions=t=>{var s,i;const h={debug:!1,initialOffset:0,overscan:1,paddingStart:0,paddingEnd:0,scrollPaddingStart:0,scrollPaddingEnd:0,horizontal:!1,getItemKey:r,rangeExtractor:o,onChange:()=>{},measureElement:f,initialRect:{width:0,height:0},scrollMargin:0,gap:0,indexAttribute:"data-index",initialMeasurementsCache:[],lanes:1,anchorTo:"start",followOnAppend:!1,scrollEndThreshold:1,isScrollingResetDelay:150,enabled:!0,isRtl:!1,useScrollendEvent:!1,useAnimationFrameWithResizeObserver:!1,laneAssignmentMode:"estimate"};for(const s in t){const i=t[s];void 0!==i&&(h[s]=i)}const n=this.options;let e=null,l=null;if(void 0!==n&&n.enabled&&h.enabled&&"end"===h.anchorTo&&null!==this.scrollElement){const t=n.count,r=h.count,o=this.getMeasurements(),u=t>0?(null==(s=o[0])?void 0:s.key)??n.getItemKey(0):null,a=t>0?(null==(i=o[t-1])?void 0:i.key)??n.getItemKey(t-1):null;if(r!==t||t>0&&r>0&&(h.getItemKey(0)!==u||h.getItemKey(r-1)!==a)){const s=t>0?this.getVirtualItemForOffset(this.getScrollOffset())??o[0]:null;s&&(e=[s.key,this.getScrollOffset()-s.start]);const i=!0===h.followOnAppend?"auto":h.followOnAppend||null;i&&r>t&&this.isAtEnd(n.scrollEndThreshold)&&(0===t||h.getItemKey(r-1)!==a)&&(l=i)}}this.options=h,(e||l)&&(this.pendingScrollAnchor=[(null==e?void 0:e[0])??null,(null==e?void 0:e[1])??0,l])},this.notify=t=>{var s,i;null==(i=(s=this.options).onChange)||i.call(s,this,t)},this.maybeNotify=t((()=>(this.calculateRange(),[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null])),(t=>{this.notify(t)}),{key:!1,debug:()=>this.options.debug,initialDeps:[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]}),this.cleanup=()=>{this.unsubs.filter(Boolean).forEach((t=>t())),this.unsubs=[],this.observer.disconnect(),null!=this.rafId&&this.targetWindow&&(this.targetWindow.cancelAnimationFrame(this.rafId),this.rafId=null),this.scrollState=null,this.scrollElement=null,this.targetWindow=null},this._didMount=()=>()=>{this.cleanup()},this._willUpdate=()=>{var t;const s=this.options.enabled?this.options.getScrollElement():null;if(this.scrollElement!==s){if(this.cleanup(),!s)return void this.maybeNotify();if(this.scrollElement=s,this.scrollElement&&"ownerDocument"in this.scrollElement?this.targetWindow=this.scrollElement.ownerDocument.defaultView:this.targetWindow=(null==(t=this.scrollElement)?void 0:t.window)??null,this.elementsCache.forEach((t=>{this.observer.observe(t)})),this.unsubs.push(this.options.observeElementRect(this,(t=>{this.scrollRect=t,this.maybeNotify()}))),this.unsubs.push(this.options.observeElementOffset(this,((t,s)=>{null!==this._intendedScrollOffset&&Math.abs(t-this._intendedScrollOffset)<1.5&&(t=this._intendedScrollOffset),this._intendedScrollOffset=null,this.scrollAdjustments=0,this.scrollDirection=s?this.getScrollOffset()<t?"forward":"backward":null,this.scrollOffset=t,this.isScrolling=s,this._flushIosDeferredIfReady(),this.scrollState&&this.scheduleScrollReconcile(),this.maybeNotify()}))),"addEventListener"in this.scrollElement){const t=this.scrollElement,s=()=>{this._iosTouching=!0,this._iosJustTouchEnded=!1,null!==this._iosTouchEndTimerId&&null!=this.targetWindow&&(this.targetWindow.clearTimeout(this._iosTouchEndTimerId),this._iosTouchEndTimerId=null)},i=()=>{this._iosTouching=!1,e()&&null!=this.targetWindow&&(this._iosJustTouchEnded=!0,this._iosTouchEndTimerId=this.targetWindow.setTimeout((()=>{this._iosJustTouchEnded=!1,this._iosTouchEndTimerId=null,this._flushIosDeferredIfReady()}),150))};t.addEventListener("touchstart",s,a),t.addEventListener("touchend",i,a),this.unsubs.push((()=>{t.removeEventListener("touchstart",s),t.removeEventListener("touchend",i),null!==this._iosTouchEndTimerId&&null!=this.targetWindow&&(this.targetWindow.clearTimeout(this._iosTouchEndTimerId),this._iosTouchEndTimerId=null)}))}this._scrollToOffset(this.getScrollOffset(),{adjustments:void 0,behavior:void 0})}const h=this.pendingScrollAnchor;if(this.pendingScrollAnchor=null,h&&this.scrollElement&&this.options.enabled){const[t,s,n]=h;if(null!==t){const{count:h,getItemKey:n}=this.options;let e=0;for(;e<h&&n(e)!==t;)e++;const l=e<h?this.getMeasurements()[e]:void 0;if(l){const t=l.start+s-this.getScrollOffset();i(t,0)||this.applyScrollAdjustment(t)}}n&&this.scrollToEnd({behavior:n})}},this._flushIosDeferredIfReady=()=>{if(0===this._iosDeferredAdjustment)return;if(this.isScrolling)return;if(this._iosTouching)return;if(this._iosJustTouchEnded)return;const t=this.getScrollOffset(),s=this.getMaxScrollOffset();if(t<0||t>s)return;const i=this._iosDeferredAdjustment;this._iosDeferredAdjustment=0,this._scrollToOffset(t,{adjustments:this.scrollAdjustments+=i,behavior:void 0})},this.rafId=null,this.getSize=()=>this.options.enabled?(this.scrollRect=this.scrollRect??this.options.initialRect,this.scrollRect[this.options.horizontal?"width":"height"]):(this.scrollRect=null,0),this.getScrollOffset=()=>this.options.enabled?(this.scrollOffset=this.scrollOffset??("function"==typeof this.options.initialOffset?this.options.initialOffset():this.options.initialOffset),this.scrollOffset):(this.scrollOffset=null,0),this.getFurthestMeasurement=(t,s)=>{const i=new Map,h=new Map;for(let n=s-1;n>=0;n--){const s=t[n];if(i.has(s.lane))continue;const e=h.get(s.lane);if(null==e||s.end>e.end?h.set(s.lane,s):s.end<e.end&&i.set(s.lane,!0),i.size===this.options.lanes)break}return h.size===this.options.lanes?Array.from(h.values()).sort(((t,s)=>t.end===s.end?t.index-s.index:t.end-s.end))[0]:void 0},this.getMeasurementOptions=t((()=>[this.options.count,this.options.paddingStart,this.options.scrollMargin,this.options.getItemKey,this.options.enabled,this.options.lanes,this.options.laneAssignmentMode]),((t,s,i,h,n,e,l)=>(void 0!==this.prevLanes&&this.prevLanes!==e&&(this.lanesChangedFlag=!0),this.prevLanes=e,this.pendingMin=null,{count:t,paddingStart:s,scrollMargin:i,getItemKey:h,enabled:n,lanes:e,laneAssignmentMode:l})),{key:!1}),this.getMeasurements=t((()=>[this.getMeasurementOptions(),this.itemSizeCacheVersion]),(({count:t,paddingStart:s,scrollMargin:i,getItemKey:h,enabled:n,lanes:e,laneAssignmentMode:l},r)=>{const o=this.itemSizeCache;if(!n)return this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),[];if(this.laneAssignments.size>t)for(const s of this.laneAssignments.keys())s>=t&&this.laneAssignments.delete(s);this.lanesChangedFlag&&(this.lanesChangedFlag=!1,this.lanesSettling=!0,this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),this.pendingMin=null),0!==this.measurementsCache.length||this.lanesSettling||(this.measurementsCache=this.options.initialMeasurementsCache,this.measurementsCache.forEach((t=>{this.itemSizeCache.set(t.key,t.size)})));const u=this.lanesSettling?0:this.pendingMin??0;if(this.pendingMin=null,this.lanesSettling&&this.measurementsCache.length===t&&(this.lanesSettling=!1),1===e){const n=this.options.gap,e=2*t;let l,r=this._flatMeasurements;if(!r||r.length<e){const t=new Float64Array(e);r&&u>0&&t.set(r.subarray(0,2*u)),r=t,this._flatMeasurements=r}if(0===u)l=s+i;else{const t=u-1;l=r[2*t]+r[2*t+1]+n}for(let s=u;s<t;s++){const t=h(s),i=o.get(t),e="number"==typeof i?i:this.options.estimateSize(s);r[2*s]=l,r[2*s+1]=e,l+=e+n}const a=function(t,s,i){const h=new Array(t);return new Proxy(h,{get(h,n,e){if("string"==typeof n){const e=n.charCodeAt(0);if(e>=48&&e<=57){const e=+n;if(Number.isInteger(e)&&e>=0&&e<t){let t=h[e];if(!t){const n=s[2*e];t=h[e]={index:e,key:i(e),start:n,size:s[2*e+1],end:n+s[2*e+1],lane:0}}return t}}if("length"===n)return t}return Reflect.get(h,n,e)}})}(t,r,h);return this.measurementsCache=a,a}const a=this.measurementsCache.slice(0,u),c=new Array(e).fill(void 0);for(let t=0;t<u;t++){const s=a[t];s&&(c[s.lane]=t)}for(let n=u;n<t;n++){const t=h(n),e=this.laneAssignments.get(n);let r,u;const d="estimate"===l||o.has(t);if(void 0!==e&&this.options.lanes>1){r=e;const t=c[r],h=void 0!==t?a[t]:void 0;u=h?h.end+this.options.gap:s+i}else{const t=1===this.options.lanes?a[n-1]:this.getFurthestMeasurement(a,n);u=t?t.end+this.options.gap:s+i,r=t?t.lane:n%this.options.lanes,this.options.lanes>1&&d&&this.laneAssignments.set(n,r)}const f=o.get(t),v="number"==typeof f?f:this.options.estimateSize(n),b=u+v;a[n]={index:n,start:u,size:v,end:b,key:t,lane:r},c[r]=n}return this.measurementsCache=a,a}),{key:!1,debug:()=>this.options.debug}),this.calculateRange=t((()=>[this.getMeasurements(),this.getSize(),this.getScrollOffset(),this.options.lanes]),((t,s,i,h)=>this.range=t.length>0&&s>0?function({measurements:t,outerSize:s,scrollOffset:i,lanes:h,flat:n}){const e=t.length-1,l=n?t=>n[2*t]:s=>t[s].start,r=n?t=>n[2*t]+n[2*t+1]:s=>t[s].end;if(t.length<=h)return{startIndex:0,endIndex:e};let o=g(0,e,l,i),u=o;if(1===h)for(;u<e&&r(u)<i+s;)u++;else if(h>1){const n=Array(h).fill(0);for(;u<e&&n.some((t=>t<i+s));){const s=t[u];n[s.lane]=s.end,u++}const l=Array(h).fill(i+s);for(;o>=0&&l.some((t=>t>=i));){const s=t[o];l[s.lane]=s.start,o--}o=Math.max(0,o-o%h),u=Math.min(e,u+(h-1-u%h))}return{startIndex:o,endIndex:u}}({measurements:t,outerSize:s,scrollOffset:i,lanes:h,flat:1===h&&null!=this._flatMeasurements?this._flatMeasurements:null}):null),{key:!1,debug:()=>this.options.debug}),this.getVirtualIndexes=t((()=>{let t=null,s=null;const i=this.calculateRange();return i&&(t=i.startIndex,s=i.endIndex),this.maybeNotify.updateDeps([this.isScrolling,t,s]),[this.options.rangeExtractor,this.options.overscan,this.options.count,t,s]}),((t,s,i,h,n)=>null===h||null===n?[]:t({startIndex:h,endIndex:n,overscan:s,count:i})),{key:!1,debug:()=>this.options.debug}),this.indexFromElement=t=>{const s=this.options.indexAttribute,i=t.getAttribute(s);return i?parseInt(i,10):(console.warn(`Missing attribute name '${s}={index}' on measured element.`),-1)},this.shouldMeasureDuringScroll=t=>{var s;if(!this.scrollState||"smooth"!==this.scrollState.behavior)return!0;const i=this.scrollState.index??(null==(s=this.getVirtualItemForOffset(this.scrollState.lastTargetOffset))?void 0:s.index);if(void 0!==i&&this.range){const s=Math.max(this.options.overscan,Math.ceil((this.range.endIndex-this.range.startIndex)/2)),h=Math.max(0,i-s),n=Math.min(this.options.count-1,i+s);return t>=h&&t<=n}return!0},this.measureElement=t=>{if(!t)return void this.elementsCache.forEach(((t,s)=>{t.isConnected||(this.observer.unobserve(t),this.elementsCache.delete(s))}));const s=this.indexFromElement(t),i=this.options.getItemKey(s),h=this.elementsCache.get(i);h!==t&&(h&&this.observer.unobserve(h),this.observer.observe(t),this.elementsCache.set(i,t)),this.isScrolling&&!this.scrollState||!this.shouldMeasureDuringScroll(s)||this.resizeItem(s,this.options.measureElement(t,void 0,this))},this.resizeItem=(t,s)=>{var i,h;if(t<0||t>=this.options.count)return;let n,e,l;const r=this._flatMeasurements;if(1===this.options.lanes&&null!==r)l=this.options.getItemKey(t),e=r[2*t],n=r[2*t+1];else{const s=this.measurementsCache[t];if(!s)return;l=s.key,e=s.start,n=s.size}const o=s-(this.itemSizeCache.get(l)??n);if(0!==o){const r="end"===this.options.anchorTo&&"smooth"!==(null==(i=this.scrollState)?void 0:i.behavior)&&this.getVirtualDistanceFromEnd()<=this.options.scrollEndThreshold,u=r?this.getTotalSize():0,a="smooth"!==(null==(h=this.scrollState)?void 0:h.behavior)&&(void 0!==this.shouldAdjustScrollPositionOnItemSizeChange?this.shouldAdjustScrollPositionOnItemSizeChange(this.measurementsCache[t]??{index:t,key:l,start:e,size:n,end:e+n,lane:0},o,this):e<this.getScrollOffset()+this.scrollAdjustments&&"backward"!==this.scrollDirection);(null===this.pendingMin||t<this.pendingMin)&&(this.pendingMin=t),this.itemSizeCache.set(l,s),this.itemSizeCacheVersion++,r?this.applyScrollAdjustment(this.getTotalSize()-u):a&&this.applyScrollAdjustment(o),this.notify(!1)}},this.getVirtualItems=t((()=>[this.getVirtualIndexes(),this.getMeasurements()]),((t,s)=>{const i=[];for(let h=0,n=t.length;h<n;h++){const n=s[t[h]];i.push(n)}return i}),{key:!1,debug:()=>this.options.debug}),this.getVirtualItemForOffset=t=>{const i=this.getMeasurements();if(0===i.length)return;const h=this._flatMeasurements,n=1===this.options.lanes&&null!=h,e=g(0,i.length-1,n?t=>h[2*t]:t=>s(i[t]).start,t);return s(i[e])},this.getMaxScrollOffset=()=>{if(!this.scrollElement)return 0;if("scrollHeight"in this.scrollElement)return this.options.horizontal?this.scrollElement.scrollWidth-this.scrollElement.clientWidth:this.scrollElement.scrollHeight-this.scrollElement.clientHeight;{const t=this.scrollElement.document.documentElement;return this.options.horizontal?t.scrollWidth-this.scrollElement.innerWidth:t.scrollHeight-this.scrollElement.innerHeight}},this.getVirtualDistanceFromEnd=()=>Math.max(this.getTotalSize()-this.getSize()-this.getScrollOffset(),0),this.getDistanceFromEnd=()=>Math.max(this.getMaxScrollOffset()-this.getScrollOffset(),0),this.isAtEnd=(t=this.options.scrollEndThreshold)=>this.getDistanceFromEnd()<=t,this.getOffsetForAlignment=(t,s,i=0)=>{if(!this.scrollElement)return 0;const h=this.getSize(),n=this.getScrollOffset();"auto"===s&&(s=t>=n+h?"end":"start"),"center"===s?t+=(i-h)/2:"end"===s&&(t-=h);const e=this.getMaxScrollOffset();return Math.max(Math.min(e,t),0)},this.getOffsetForIndex=(t,s="auto")=>{t=Math.max(0,Math.min(t,this.options.count-1));const i=this.getSize(),h=this.getScrollOffset(),n=this.measurementsCache[t];if(!n)return;if("auto"===s)if(n.end>=h+i-this.options.scrollPaddingEnd)s="end";else{if(!(n.start<=h+this.options.scrollPaddingStart))return[h,s];s="start"}if("end"===s&&t===this.options.count-1)return[this.getMaxScrollOffset(),s];const e="end"===s?n.end+this.options.scrollPaddingEnd:n.start-this.options.scrollPaddingStart;return[this.getOffsetForAlignment(e,s,n.size),s]},this.scrollToOffset=(t,{align:s="start",behavior:i="auto"}={})=>{const h=this.getOffsetForAlignment(t,s),n=this.now();this.scrollState={index:null,align:s,behavior:i,startedAt:n,lastTargetOffset:h,stableFrames:0},this._scrollToOffset(h,{adjustments:void 0,behavior:i}),this.scheduleScrollReconcile()},this.scrollToIndex=(t,{align:s="auto",behavior:i="auto"}={})=>{t=Math.max(0,Math.min(t,this.options.count-1));const h=this.getOffsetForIndex(t,s);if(!h)return;const[n,e]=h,l=this.now();this.scrollState={index:t,align:e,behavior:i,startedAt:l,lastTargetOffset:n,stableFrames:0},this._scrollToOffset(n,{adjustments:void 0,behavior:i}),this.scheduleScrollReconcile()},this.scrollBy=(t,{behavior:s="auto"}={})=>{const i=this.getScrollOffset()+t,h=this.now();this.scrollState={index:null,align:"start",behavior:s,startedAt:h,lastTargetOffset:i,stableFrames:0},this._scrollToOffset(i,{adjustments:void 0,behavior:s}),this.scheduleScrollReconcile()},this.scrollToEnd=({behavior:t="auto"}={})=>{this.options.count>0?this.scrollToIndex(this.options.count-1,{align:"end",behavior:t}):this.scrollToOffset(Math.max(this.getTotalSize()-this.getSize(),0),{behavior:t})},this.getTotalSize=()=>{var t;const s=this.getMeasurements();let i;if(0===s.length)i=this.options.paddingStart;else if(1===this.options.lanes){const h=s.length-1,n=this._flatMeasurements;i=null!=n?n[2*h]+n[2*h+1]:(null==(t=s[h])?void 0:t.end)??0}else{const t=Array(this.options.lanes).fill(null);let h=s.length-1;for(;h>=0&&t.some((t=>null===t));){const i=s[h];null===t[i.lane]&&(t[i.lane]=i.end),h--}i=Math.max(...t.filter((t=>null!==t)))}return Math.max(i-this.options.scrollMargin+this.options.paddingEnd,0)},this.takeSnapshot=()=>{const t=[];if(0===this.itemSizeCache.size)return t;const s=this.getMeasurements();for(const i of s)i&&this.itemSizeCache.has(i.key)&&t.push({index:i.index,key:i.key,start:i.start,size:i.size,end:i.end,lane:i.lane});return t},this._scrollToOffset=(t,{adjustments:s,behavior:i})=>{this._intendedScrollOffset=t+(s??0),this.options.scrollToFn(t,{behavior:i,adjustments:s},this)},this.measure=()=>{this.pendingMin=null,this.itemSizeCache.clear(),this.laneAssignments.clear(),this.itemSizeCacheVersion++,this.notify(!1)},this.setOptions(h)}applyScrollAdjustment(t,s){0!==t&&(e()&&(this.isScrolling||this._iosTouching||this._iosJustTouchEnded)?this._iosDeferredAdjustment+=t:this._scrollToOffset(this.getScrollOffset(),{adjustments:this.scrollAdjustments+=t,behavior:s}))}scheduleScrollReconcile(){this.targetWindow?null==this.rafId&&(this.rafId=this.targetWindow.requestAnimationFrame((()=>{this.rafId=null,this.reconcileScroll()}))):this.scrollState=null}reconcileScroll(){if(!this.scrollState)return;if(!this.scrollElement)return;if(this.now()-this.scrollState.startedAt>5e3)return void(this.scrollState=null);const t=null!=this.scrollState.index?this.getOffsetForIndex(this.scrollState.index,this.scrollState.align):void 0,s=t?t[0]:this.scrollState.lastTargetOffset,h=s!==this.scrollState.lastTargetOffset;if(!h&&i(s,this.getScrollOffset())){if(this.scrollState.stableFrames++,this.scrollState.stableFrames>=1)return this.getScrollOffset()!==s&&this._scrollToOffset(s,{adjustments:void 0,behavior:"auto"}),void(this.scrollState=null)}else if(this.scrollState.stableFrames=0,h){const t=this.getSize()||600,i=Math.abs(s-this.getScrollOffset()),h="smooth"===this.scrollState.behavior&&i>t;this.scrollState.lastTargetOffset=s,h||(this.scrollState.behavior="auto"),this._scrollToOffset(s,{adjustments:void 0,behavior:h?"smooth":"auto"})}this.scheduleScrollReconcile()}}const g=(t,s,i,h)=>{for(;t<=s;){const n=(t+s)/2|0,e=i(n);if(e<h)t=n+1;else{if(!(e>h))return n;s=n-1}}return t>0?t-1:0};class m{constructor(t,s){this.cleanup=()=>{};const i={...s,onChange:(t,i)=>{var h;this.host.updateComplete.then((()=>this.host.requestUpdate())),null==(h=s.onChange)||h.call(s,t,i)}};this.virtualizer=new b(i),(this.host=t).addController(this)}getVirtualizer(){return this.virtualizer}hostConnected(){this.cleanup=this.virtualizer._didMount()}hostUpdated(){this.virtualizer._willUpdate()}hostDisconnected(){this.cleanup()}}class M extends m{constructor(t,s){super(t,{observeElementRect:u,observeElementOffset:d,scrollToFn:v,...s})}}export{M as V};