@acorex/components 20.7.29 → 20.7.31

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.
@@ -6,7 +6,7 @@ import { AXUnsubscriber, AXHtmlUtil } from '@acorex/core/utils';
6
6
  import { AXZIndexService } from '@acorex/core/z-index';
7
7
  import { isPlatformBrowser, NgTemplateOutlet, AsyncPipe } from '@angular/common';
8
8
  import * as i0 from '@angular/core';
9
- import { Injectable, signal, inject, Renderer2, computed, output, input, afterNextRender, HostBinding, HostListener, ChangeDetectionStrategy, ViewEncapsulation, Component, DOCUMENT, PLATFORM_ID, Injector, DestroyRef, NgModule } from '@angular/core';
9
+ import { Injectable, signal, inject, Renderer2, Injector, computed, output, input, afterNextRender, HostBinding, HostListener, ChangeDetectionStrategy, ViewEncapsulation, Component, DOCUMENT, PLATFORM_ID, DestroyRef, NgModule } from '@angular/core';
10
10
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
11
11
  import { Router, NavigationStart } from '@angular/router';
12
12
  import { filter } from 'rxjs/operators';
@@ -33,8 +33,6 @@ class AXRootMenu {
33
33
  }
34
34
  class AXMenuItemComponentBase {
35
35
  }
36
- class AXMenuItem {
37
- }
38
36
  class AXMenuItemClickBaseEvent extends NXClickEvent {
39
37
  constructor() {
40
38
  super(...arguments);
@@ -58,6 +56,7 @@ class AXMenuItemComponent extends NXComponent {
58
56
  this.scrollableParents = [];
59
57
  this.unsuscriber = inject(AXUnsubscriber);
60
58
  this.renderer = inject(Renderer2);
59
+ this.injector = inject(Injector);
61
60
  this.zIndexService = inject(AXZIndexService);
62
61
  this.zToken = null;
63
62
  this.arrowIcon = computed(() => {
@@ -84,7 +83,7 @@ class AXMenuItemComponent extends NXComponent {
84
83
  this.service.open$.pipe(this.unsuscriber.takeUntilDestroy).subscribe((item) => {
85
84
  if (this === item) {
86
85
  this.isOpen.set(true);
87
- this.calculatePosition();
86
+ this.schedulePositionCalculation();
88
87
  }
89
88
  });
90
89
  //
@@ -173,6 +172,17 @@ class AXMenuItemComponent extends NXComponent {
173
172
  this.zIndexService.release(this.zToken);
174
173
  this.zToken = null;
175
174
  }
175
+ /**
176
+ * Positions the submenu after layout so nested levels measure their full height.
177
+ */
178
+ schedulePositionCalculation() {
179
+ this.calculatePosition();
180
+ afterNextRender(() => {
181
+ if (this.isOpen()) {
182
+ this.calculatePosition();
183
+ }
184
+ }, { injector: this.injector });
185
+ }
176
186
  /**
177
187
  * Calculate the position of the submenu to avoid it going out of the viewport.
178
188
  */
@@ -180,16 +190,10 @@ class AXMenuItemComponent extends NXComponent {
180
190
  const submenu = this.nativeElement.querySelector('.ax-menu-items');
181
191
  if (!submenu)
182
192
  return;
183
- // Temporarily show the menu to measure its dimensions, but keep it invisible
184
- this.renderer.setStyle(submenu, 'visibility', 'hidden');
185
- this.renderer.setStyle(submenu, 'display', 'block');
186
- const submenuRect = submenu.getBoundingClientRect();
193
+ const submenuRect = this.measureSubmenuRect(submenu);
187
194
  const itemRect = this.nativeElement.getBoundingClientRect();
188
195
  const windowWidth = window.innerWidth;
189
196
  const windowHeight = window.innerHeight;
190
- // Reset temporary styles
191
- this.renderer.removeStyle(submenu, 'visibility');
192
- this.renderer.removeStyle(submenu, 'display');
193
197
  const isRtl = AXHtmlUtil.isRtl(this.nativeElement);
194
198
  let finalTop;
195
199
  let finalLeft;
@@ -201,15 +205,13 @@ class AXMenuItemComponent extends NXComponent {
201
205
  if (preferredTop + submenuRect.height <= windowHeight) {
202
206
  finalTop = preferredTop;
203
207
  }
204
- else if (this.isFirstLevel() && this.root.orientation() === 'horizontal' && alternateTop >= 0) {
208
+ else if (alternateTop >= 0) {
205
209
  finalTop = alternateTop;
206
210
  }
207
211
  else {
208
212
  finalTop = windowHeight - submenuRect.height;
209
213
  }
210
- if (finalTop < 0) {
211
- finalTop = 0;
212
- }
214
+ finalTop = this.clampVerticalPosition(finalTop, submenuRect.height, windowHeight);
213
215
  // --- 2. HORIZONTAL POSITIONING ---
214
216
  if (this.isFirstLevel() && this.root.orientation() === 'horizontal') {
215
217
  const preferredLeft = isRtl ? itemRect.right - submenuRect.width : itemRect.left;
@@ -249,7 +251,7 @@ class AXMenuItemComponent extends NXComponent {
249
251
  }
250
252
  }
251
253
  }
252
- // --- 3. NEW: OVERLAP-AWARE VERTICAL SHIFT ---
254
+ // --- 3. OVERLAP-AWARE VERTICAL SHIFT ---
253
255
  const isNestedMenu = !(this.isFirstLevel() && this.root.orientation() === 'horizontal');
254
256
  if (isNestedMenu) {
255
257
  const overlapsParent = finalLeft < itemRect.right && itemRect.left < finalLeft + submenuRect.width;
@@ -260,6 +262,7 @@ class AXMenuItemComponent extends NXComponent {
260
262
  }
261
263
  }
262
264
  }
265
+ finalTop = this.clampVerticalPosition(finalTop, submenuRect.height, windowHeight);
263
266
  // --- 4. APPLY FINAL STYLES ---
264
267
  this.renderer.setStyle(submenu, 'position', 'fixed');
265
268
  this.renderer.setStyle(submenu, 'top', `${finalTop}px`);
@@ -269,6 +272,25 @@ class AXMenuItemComponent extends NXComponent {
269
272
  this.renderer.setStyle(submenu, 'z-index', String(this.zToken.zIndex));
270
273
  }
271
274
  }
275
+ /** Measures submenu size using the same flex layout as the rendered menu. */
276
+ measureSubmenuRect(submenu) {
277
+ this.renderer.setStyle(submenu, 'visibility', 'hidden');
278
+ this.renderer.setStyle(submenu, 'display', 'flex');
279
+ const rect = submenu.getBoundingClientRect();
280
+ this.renderer.removeStyle(submenu, 'visibility');
281
+ this.renderer.removeStyle(submenu, 'display');
282
+ return rect;
283
+ }
284
+ /** Keeps the submenu fully inside the viewport vertically. */
285
+ clampVerticalPosition(top, height, windowHeight) {
286
+ if (top + height > windowHeight) {
287
+ top = windowHeight - height;
288
+ }
289
+ if (top < 0) {
290
+ top = 0;
291
+ }
292
+ return top;
293
+ }
272
294
  handleClick(e) {
273
295
  e.stopPropagation();
274
296
  if (this.disabled())
@@ -862,5 +884,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
862
884
  * Generated bundle index. Do not edit.
863
885
  */
864
886
 
865
- export { AXContextMenuComponent, AXContextMenuOpeningEvent, AXMenuComponent, AXMenuItem, AXMenuItemClickBaseEvent, AXMenuItemComponent, AXMenuItemComponentBase, AXMenuModule, AXMenuService, AXRootMenu };
887
+ export { AXContextMenuComponent, AXContextMenuOpeningEvent, AXMenuComponent, AXMenuItemClickBaseEvent, AXMenuItemComponent, AXMenuItemComponentBase, AXMenuModule, AXMenuService, AXRootMenu };
866
888
  //# sourceMappingURL=acorex-components-menu.mjs.map