@radix-ng/primitives 0.50.0 → 1.0.0-beta.0
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/collection/README.md +1 -0
- package/fesm2022/radix-ng-primitives-accordion.mjs +134 -66
- package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-alert-dialog.mjs +224 -132
- package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-arrow.mjs +26 -10
- package/fesm2022/radix-ng-primitives-arrow.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-aspect-ratio.mjs +6 -6
- package/fesm2022/radix-ng-primitives-aspect-ratio.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-avatar.mjs +68 -75
- package/fesm2022/radix-ng-primitives-avatar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-button.mjs +123 -0
- package/fesm2022/radix-ng-primitives-button.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-calendar.mjs +104 -103
- package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-checkbox.mjs +414 -80
- package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-collapsible.mjs +193 -92
- package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-collection.mjs +72 -0
- package/fesm2022/radix-ng-primitives-collection.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-config.mjs +5 -5
- package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-context-menu.mjs +143 -427
- package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-core.mjs +757 -757
- package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-cropper.mjs +55 -53
- package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-date-field.mjs +93 -86
- package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-dialog.mjs +658 -330
- package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +98 -76
- package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-drawer.mjs +1059 -0
- package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-editable.mjs +20 -20
- package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-field.mjs +363 -0
- package/fesm2022/radix-ng-primitives-field.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-fieldset.mjs +79 -0
- package/fesm2022/radix-ng-primitives-fieldset.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-focus-guards.mjs +3 -3
- package/fesm2022/radix-ng-primitives-focus-guards.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-focus-scope.mjs +29 -14
- package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-input.mjs +172 -0
- package/fesm2022/radix-ng-primitives-input.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-label.mjs +11 -11
- package/fesm2022/radix-ng-primitives-label.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menu.mjs +1484 -353
- package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menubar.mjs +290 -162
- package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-meter.mjs +271 -0
- package/fesm2022/radix-ng-primitives-meter.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs +1060 -1553
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-number-field.mjs +1102 -366
- package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-pagination.mjs +51 -51
- package/fesm2022/radix-ng-primitives-pagination.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popover.mjs +980 -995
- package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popper.mjs +137 -82
- package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-portal.mjs +40 -16
- package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-presence.mjs +134 -246
- package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-preview-card.mjs +997 -0
- package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-progress.mjs +231 -92
- package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-radio.mjs +211 -70
- package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-roving-focus.mjs +127 -77
- package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-select.mjs +791 -511
- package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-separator.mjs +16 -45
- package/fesm2022/radix-ng-primitives-separator.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-slider.mjs +976 -720
- package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-stepper.mjs +69 -71
- package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-switch.mjs +128 -124
- package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tabs.mjs +388 -115
- package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-time-field.mjs +111 -117
- package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toggle-group.mjs +122 -248
- package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toggle.mjs +99 -62
- package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toolbar.mjs +307 -94
- package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tooltip.mjs +690 -1079
- package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-visually-hidden.mjs +46 -87
- package/fesm2022/radix-ng-primitives-visually-hidden.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives.mjs.map +1 -1
- package/meter/README.md +3 -0
- package/navigation-menu/README.md +2 -1
- package/package.json +85 -63
- package/portal/README.md +2 -0
- package/preview-card/README.md +3 -0
- package/schematics/collection.json +1 -0
- package/schematics/ng-add/index.d.ts +3 -2
- package/schematics/ng-add/index.js +62 -31
- package/schematics/ng-add/index.js.map +1 -1
- package/schematics/ng-add/package-config.d.ts +4 -2
- package/schematics/ng-add/package-config.js +10 -2
- package/schematics/ng-add/package-config.js.map +1 -1
- package/schematics/ng-add/schema.d.ts +3 -0
- package/schematics/ng-add/schema.js +3 -0
- package/schematics/ng-add/schema.js.map +1 -0
- package/schematics/ng-add/schema.json +14 -0
- package/select/README.md +2 -0
- package/{accordion/index.d.ts → types/radix-ng-primitives-accordion.d.ts} +102 -67
- package/types/radix-ng-primitives-alert-dialog.d.ts +114 -0
- package/{arrow/index.d.ts → types/radix-ng-primitives-arrow.d.ts} +1 -1
- package/{aspect-ratio/index.d.ts → types/radix-ng-primitives-aspect-ratio.d.ts} +1 -1
- package/{avatar/index.d.ts → types/radix-ng-primitives-avatar.d.ts} +7 -11
- package/types/radix-ng-primitives-button.d.ts +73 -0
- package/{calendar/index.d.ts → types/radix-ng-primitives-calendar.d.ts} +2 -3
- package/types/radix-ng-primitives-checkbox.d.ts +337 -0
- package/types/radix-ng-primitives-collapsible.d.ts +159 -0
- package/types/radix-ng-primitives-collection.d.ts +44 -0
- package/{config/index.d.ts → types/radix-ng-primitives-config.d.ts} +1 -1
- package/types/radix-ng-primitives-context-menu.d.ts +73 -0
- package/{core/index.d.ts → types/radix-ng-primitives-core.d.ts} +311 -236
- package/{cropper/index.d.ts → types/radix-ng-primitives-cropper.d.ts} +6 -5
- package/{date-field/index.d.ts → types/radix-ng-primitives-date-field.d.ts} +42 -27
- package/types/radix-ng-primitives-dialog.d.ts +323 -0
- package/{dismissable-layer/index.d.ts → types/radix-ng-primitives-dismissable-layer.d.ts} +15 -7
- package/types/radix-ng-primitives-drawer.d.ts +448 -0
- package/{editable/index.d.ts → types/radix-ng-primitives-editable.d.ts} +1 -1
- package/types/radix-ng-primitives-field.d.ts +373 -0
- package/types/radix-ng-primitives-fieldset.d.ts +48 -0
- package/{focus-scope/index.d.ts → types/radix-ng-primitives-focus-scope.d.ts} +13 -5
- package/types/radix-ng-primitives-input.d.ts +87 -0
- package/{label/index.d.ts → types/radix-ng-primitives-label.d.ts} +0 -1
- package/types/radix-ng-primitives-menu.d.ts +612 -0
- package/types/radix-ng-primitives-menubar.d.ts +66 -0
- package/types/radix-ng-primitives-meter.d.ts +193 -0
- package/types/radix-ng-primitives-navigation-menu.d.ts +488 -0
- package/types/radix-ng-primitives-number-field.d.ts +464 -0
- package/{pagination/index.d.ts → types/radix-ng-primitives-pagination.d.ts} +2 -2
- package/types/radix-ng-primitives-popover.d.ts +416 -0
- package/{popper/index.d.ts → types/radix-ng-primitives-popper.d.ts} +50 -9
- package/types/radix-ng-primitives-portal.d.ts +30 -0
- package/types/radix-ng-primitives-presence.d.ts +55 -0
- package/types/radix-ng-primitives-preview-card.d.ts +359 -0
- package/types/radix-ng-primitives-progress.d.ts +206 -0
- package/{radio/index.d.ts → types/radix-ng-primitives-radio.d.ts} +56 -26
- package/{roving-focus/index.d.ts → types/radix-ng-primitives-roving-focus.d.ts} +38 -27
- package/types/radix-ng-primitives-select.d.ts +512 -0
- package/types/radix-ng-primitives-separator.d.ts +38 -0
- package/types/radix-ng-primitives-slider.d.ts +377 -0
- package/{stepper/index.d.ts → types/radix-ng-primitives-stepper.d.ts} +21 -22
- package/types/radix-ng-primitives-switch.d.ts +121 -0
- package/types/radix-ng-primitives-tabs.d.ts +247 -0
- package/{time-field/index.d.ts → types/radix-ng-primitives-time-field.d.ts} +46 -31
- package/types/radix-ng-primitives-toggle-group.d.ts +116 -0
- package/types/radix-ng-primitives-toggle.d.ts +65 -0
- package/types/radix-ng-primitives-toolbar.d.ts +180 -0
- package/types/radix-ng-primitives-tooltip.d.ts +395 -0
- package/{visually-hidden/index.d.ts → types/radix-ng-primitives-visually-hidden.d.ts} +19 -19
- package/alert-dialog/index.d.ts +0 -57
- package/checkbox/index.d.ts +0 -164
- package/collapsible/index.d.ts +0 -85
- package/context-menu/index.d.ts +0 -129
- package/dialog/index.d.ts +0 -205
- package/dropdown-menu/README.md +0 -1
- package/dropdown-menu/index.d.ts +0 -171
- package/fesm2022/radix-ng-primitives-dropdown-menu.mjs +0 -583
- package/fesm2022/radix-ng-primitives-dropdown-menu.mjs.map +0 -1
- package/fesm2022/radix-ng-primitives-hover-card.mjs +0 -1246
- package/fesm2022/radix-ng-primitives-hover-card.mjs.map +0 -1
- package/fesm2022/radix-ng-primitives-tooltip2.mjs +0 -740
- package/fesm2022/radix-ng-primitives-tooltip2.mjs.map +0 -1
- package/hover-card/README.md +0 -3
- package/hover-card/index.d.ts +0 -472
- package/menu/index.d.ts +0 -139
- package/menubar/index.d.ts +0 -56
- package/navigation-menu/index.d.ts +0 -405
- package/number-field/index.d.ts +0 -203
- package/popover/index.d.ts +0 -403
- package/portal/index.d.ts +0 -22
- package/presence/index.d.ts +0 -103
- package/progress/index.d.ts +0 -79
- package/select/index.d.ts +0 -214
- package/separator/index.d.ts +0 -63
- package/slider/index.d.ts +0 -263
- package/switch/index.d.ts +0 -105
- package/tabs/index.d.ts +0 -112
- package/toggle/index.d.ts +0 -75
- package/toggle-group/index.d.ts +0 -194
- package/toolbar/index.d.ts +0 -55
- package/tooltip/index.d.ts +0 -433
- package/tooltip2/README.md +0 -3
- package/tooltip2/index.d.ts +0 -325
- /package/{focus-guards/index.d.ts → types/radix-ng-primitives-focus-guards.d.ts} +0 -0
- /package/{index.d.ts → types/radix-ng-primitives.d.ts} +0 -0
|
@@ -1,247 +1,105 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
2
|
+
import { inject, computed, Directive, input, signal, TemplateRef, booleanAttribute, effect, untracked, ElementRef, output, DestroyRef, numberAttribute, model, ViewContainerRef, Renderer2, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1 from '@radix-ng/primitives/popper';
|
|
4
|
+
import { RdxPopperContentWrapper, RdxPopperArrow, RdxPopperContent, provideRdxPopperContentConfig, RdxPopper } from '@radix-ng/primitives/popper';
|
|
5
|
+
import { createContext, ENTER, SPACE, ARROW_DOWN, ARROW_UP, HOME, END, useGraceArea, useTransitionStatus, injectDocument, ARROW_LEFT, ARROW_RIGHT, getMaxTransitionDuration } from '@radix-ng/primitives/core';
|
|
6
|
+
import * as i1$1 from '@radix-ng/primitives/roving-focus';
|
|
7
|
+
import { RdxRovingFocusGroupDirective, RdxRovingFocusItemDirective } from '@radix-ng/primitives/roving-focus';
|
|
8
|
+
import { outputFromObservable, outputToObservable } from '@angular/core/rxjs-interop';
|
|
9
|
+
import * as i2 from '@radix-ng/primitives/dismissable-layer';
|
|
10
|
+
import { RdxDismissableLayer, RdxDismissableLayersContextToken } from '@radix-ng/primitives/dismissable-layer';
|
|
11
|
+
import * as i1$2 from '@radix-ng/primitives/portal';
|
|
12
|
+
import { RdxPortal } from '@radix-ng/primitives/portal';
|
|
13
|
+
import * as i1$3 from '@radix-ng/primitives/presence';
|
|
14
|
+
import { provideRdxPresenceContext, RdxPresenceDirective } from '@radix-ng/primitives/presence';
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
onFocus(event) {
|
|
19
|
-
const prevFocusedElement = event.relatedTarget;
|
|
20
|
-
const wasTriggerFocused = prevFocusedElement === this.triggerElement;
|
|
21
|
-
const wasFocusFromContent = this.contentElement ? this.contentElement.contains(prevFocusedElement) : false;
|
|
22
|
-
if (wasTriggerFocused || !wasFocusFromContent) {
|
|
23
|
-
this.proxyFocus.emit(wasTriggerFocused ? 'start' : 'end');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxNavigationMenuFocusProxyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
27
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.3", type: RdxNavigationMenuFocusProxyComponent, isStandalone: true, selector: "rdx-navigation-menu-focus-proxy", inputs: { triggerElement: "triggerElement", contentElement: "contentElement" }, outputs: { proxyFocus: "proxyFocus" }, ngImport: i0, template: `
|
|
28
|
-
<span
|
|
29
|
-
[attr.tabindex]="0"
|
|
30
|
-
[attr.aria-hidden]="true"
|
|
31
|
-
(focus)="onFocus($event)"
|
|
32
|
-
rdxVisuallyHidden
|
|
33
|
-
feature="focusable"
|
|
34
|
-
></span>
|
|
35
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: RdxVisuallyHiddenDirective, selector: "[rdxVisuallyHidden]", inputs: ["feature"] }] }); }
|
|
36
|
-
}
|
|
37
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxNavigationMenuFocusProxyComponent, decorators: [{
|
|
38
|
-
type: Component,
|
|
39
|
-
args: [{
|
|
40
|
-
selector: 'rdx-navigation-menu-focus-proxy',
|
|
41
|
-
template: `
|
|
42
|
-
<span
|
|
43
|
-
[attr.tabindex]="0"
|
|
44
|
-
[attr.aria-hidden]="true"
|
|
45
|
-
(focus)="onFocus($event)"
|
|
46
|
-
rdxVisuallyHidden
|
|
47
|
-
feature="focusable"
|
|
48
|
-
></span>
|
|
49
|
-
`,
|
|
50
|
-
imports: [RdxVisuallyHiddenDirective]
|
|
51
|
-
}]
|
|
52
|
-
}], propDecorators: { triggerElement: [{
|
|
53
|
-
type: Input
|
|
54
|
-
}], contentElement: [{
|
|
55
|
-
type: Input
|
|
56
|
-
}], proxyFocus: [{
|
|
57
|
-
type: Output
|
|
58
|
-
}] } });
|
|
59
|
-
class RdxNavigationMenuAriaOwnsComponent {
|
|
16
|
+
const [injectNavigationMenuRootContext, provideNavigationMenuRootContext] = createContext('RdxNavigationMenuRootContext');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* An optional arrow element pointing toward the active trigger.
|
|
20
|
+
*/
|
|
21
|
+
class RdxNavigationMenuArrow {
|
|
60
22
|
constructor() {
|
|
61
|
-
this.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
23
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
24
|
+
this.wrapper = inject(RdxPopperContentWrapper, { optional: true });
|
|
25
|
+
this.side = computed(() => this.wrapper?.placedSide(), ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
26
|
+
this.align = computed(() => this.wrapper?.placedAlign(), ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
27
|
+
this.uncentered = computed(() => this.wrapper?.arrowUncentered() ?? false, ...(ngDevMode ? [{ debugName: "uncentered" }] : /* istanbul ignore next */ []));
|
|
28
|
+
}
|
|
29
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuArrow, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
30
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNavigationMenuArrow, isStandalone: true, selector: "[rdxNavigationMenuArrow]", host: { properties: { "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-side": "side()", "attr.data-align": "align()", "attr.data-uncentered": "uncentered() ? \"\" : undefined" } }, hostDirectives: [{ directive: i1.RdxPopperArrow }], ngImport: i0 }); }
|
|
67
31
|
}
|
|
68
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
69
|
-
type:
|
|
32
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuArrow, decorators: [{
|
|
33
|
+
type: Directive,
|
|
70
34
|
args: [{
|
|
71
|
-
selector: '
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
35
|
+
selector: '[rdxNavigationMenuArrow]',
|
|
36
|
+
hostDirectives: [RdxPopperArrow],
|
|
37
|
+
host: {
|
|
38
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
39
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
40
|
+
'[attr.data-side]': 'side()',
|
|
41
|
+
'[attr.data-align]': 'align()',
|
|
42
|
+
'[attr.data-uncentered]': 'uncentered() ? "" : undefined'
|
|
43
|
+
}
|
|
76
44
|
}]
|
|
77
|
-
}]
|
|
78
|
-
type: Input
|
|
79
|
-
}] } });
|
|
80
|
-
|
|
81
|
-
const RDX_NAVIGATION_MENU_TOKEN = new InjectionToken('RdxNavigationMenuToken');
|
|
82
|
-
function injectNavigationMenu() {
|
|
83
|
-
return inject(RDX_NAVIGATION_MENU_TOKEN);
|
|
84
|
-
}
|
|
85
|
-
function isRootNavigationMenu(context) {
|
|
86
|
-
return context.isRootMenu;
|
|
87
|
-
}
|
|
88
|
-
function provideNavigationMenuContext(provider) {
|
|
89
|
-
return {
|
|
90
|
-
provide: RDX_NAVIGATION_MENU_TOKEN,
|
|
91
|
-
useExisting: provider
|
|
92
|
-
};
|
|
93
|
-
}
|
|
45
|
+
}] });
|
|
94
46
|
|
|
95
|
-
var RdxNavigationMenuAnimationStatus;
|
|
96
|
-
(function (RdxNavigationMenuAnimationStatus) {
|
|
97
|
-
RdxNavigationMenuAnimationStatus["OPEN_STARTED"] = "open_started";
|
|
98
|
-
RdxNavigationMenuAnimationStatus["OPEN_ENDED"] = "open_ended";
|
|
99
|
-
RdxNavigationMenuAnimationStatus["CLOSED_STARTED"] = "closed_started";
|
|
100
|
-
RdxNavigationMenuAnimationStatus["CLOSED_ENDED"] = "closed_ended";
|
|
101
|
-
})(RdxNavigationMenuAnimationStatus || (RdxNavigationMenuAnimationStatus = {}));
|
|
102
47
|
/**
|
|
103
|
-
*
|
|
48
|
+
* An optional backdrop rendered behind the popup.
|
|
104
49
|
*/
|
|
105
|
-
class
|
|
106
|
-
|
|
107
|
-
|
|
50
|
+
class RdxNavigationMenuBackdrop {
|
|
51
|
+
constructor() {
|
|
52
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
108
53
|
}
|
|
54
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuBackdrop, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
55
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNavigationMenuBackdrop, isStandalone: true, selector: "[rdxNavigationMenuBackdrop]", host: { properties: { "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-starting-style": "rootContext.transitionStatus() === \"starting\" ? \"\" : undefined", "attr.data-ending-style": "rootContext.transitionStatus() === \"ending\" ? \"\" : undefined", "attr.data-instant": "rootContext.instant() ? \"\" : undefined", "attr.data-state": "rootContext.isOpen() ? \"open\" : \"closed\"" } }, ngImport: i0 }); }
|
|
109
56
|
}
|
|
57
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuBackdrop, decorators: [{
|
|
58
|
+
type: Directive,
|
|
59
|
+
args: [{
|
|
60
|
+
selector: '[rdxNavigationMenuBackdrop]',
|
|
61
|
+
host: {
|
|
62
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
63
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
64
|
+
'[attr.data-starting-style]': 'rootContext.transitionStatus() === "starting" ? "" : undefined',
|
|
65
|
+
'[attr.data-ending-style]': 'rootContext.transitionStatus() === "ending" ? "" : undefined',
|
|
66
|
+
'[attr.data-instant]': 'rootContext.instant() ? "" : undefined',
|
|
67
|
+
'[attr.data-state]': 'rootContext.isOpen() ? "open" : "closed"'
|
|
68
|
+
}
|
|
69
|
+
}]
|
|
70
|
+
}] });
|
|
110
71
|
|
|
111
|
-
const ROOT_CONTENT_DISMISS$1 = 'navigationMenu.rootContentDismiss';
|
|
112
72
|
/**
|
|
113
|
-
* Generate a unique
|
|
73
|
+
* Generate a short unique id segment.
|
|
114
74
|
*/
|
|
115
75
|
function generateId() {
|
|
116
76
|
return Math.random().toString(36).substring(2, 11);
|
|
117
77
|
}
|
|
118
78
|
/**
|
|
119
|
-
*
|
|
120
|
-
*/
|
|
121
|
-
function getOpenStateLabel(open) {
|
|
122
|
-
return open ? 'open' : 'closed';
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Create a trigger ID from base ID and value
|
|
126
|
-
*/
|
|
127
|
-
function makeTriggerId(baseId, value) {
|
|
128
|
-
return `${baseId}-trigger-${value}`;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Create a content ID from base ID and value
|
|
132
|
-
*/
|
|
133
|
-
function makeContentId(baseId, value) {
|
|
134
|
-
return `${baseId}-content-${value}`;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Get the motion attribute for animations
|
|
138
|
-
*/
|
|
139
|
-
function getMotionAttribute(currentValue, previousValue, itemValue, itemValues, dir) {
|
|
140
|
-
// reverse values in RTL
|
|
141
|
-
const values = dir === 'rtl' ? [...itemValues].reverse() : itemValues;
|
|
142
|
-
const currentIndex = currentValue !== null ? values.indexOf(currentValue) : -1;
|
|
143
|
-
const prevIndex = previousValue !== null ? values.indexOf(previousValue) : -1;
|
|
144
|
-
const isSelected = itemValue === currentValue;
|
|
145
|
-
const wasSelected = itemValue === previousValue && previousValue !== null;
|
|
146
|
-
// Preserve motion attribute for items not directly involved in the transition
|
|
147
|
-
// (This matches React's behaviour, using a ref/signal might be needed
|
|
148
|
-
// in the component using this function to fully replicate React's prevMotionAttributeRef)
|
|
149
|
-
// For now, returning null if not involved, as per the original code's intent here.
|
|
150
|
-
if (!isSelected && !wasSelected) {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
// handle transitions between items
|
|
154
|
-
if (currentIndex !== -1 && prevIndex !== -1) {
|
|
155
|
-
// if moving to this item (isSelected)
|
|
156
|
-
if (isSelected) {
|
|
157
|
-
return currentIndex > prevIndex ? 'from-end' : 'from-start';
|
|
158
|
-
}
|
|
159
|
-
// if moving away from this item (wasSelected)
|
|
160
|
-
if (wasSelected) {
|
|
161
|
-
return currentIndex > prevIndex ? 'to-start' : 'to-end';
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
// handle initial open (prevIndex is -1, currentIndex is valid)
|
|
165
|
-
if (isSelected && prevIndex === -1) {
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
// handle closing entirely (currentIndex is -1, prevIndex is valid)
|
|
169
|
-
if (wasSelected && currentIndex === -1) {
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
// fallback if none of the above conditions met (should ideally not happen with clear states)
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Focus the first element in a list of candidates
|
|
177
|
-
* @param candidates Array of elements that can receive focus
|
|
178
|
-
* @param preventScroll Whether to prevent scrolling when focusing
|
|
179
|
-
* @param activateKeyboardNav Whether to dispatch a dummy keydown event to activate keyboard navigation handlers
|
|
180
|
-
* @returns Whether focus was successfully moved
|
|
181
|
-
*/
|
|
182
|
-
function focusFirst(candidates, preventScroll = false, activateKeyboardNav = true) {
|
|
183
|
-
const prevFocusedElement = document.activeElement;
|
|
184
|
-
// sort candidates by tabindex to ensure proper order
|
|
185
|
-
const sortedCandidates = [...candidates].sort((a, b) => {
|
|
186
|
-
const aIndex = a.tabIndex || 0;
|
|
187
|
-
const bIndex = b.tabIndex || 0;
|
|
188
|
-
return aIndex - bIndex;
|
|
189
|
-
});
|
|
190
|
-
const success = sortedCandidates.some((candidate) => {
|
|
191
|
-
// if focus is already where we want it, do nothing
|
|
192
|
-
if (candidate === prevFocusedElement)
|
|
193
|
-
return true;
|
|
194
|
-
try {
|
|
195
|
-
candidate.focus({ preventScroll });
|
|
196
|
-
return document.activeElement !== prevFocusedElement;
|
|
197
|
-
}
|
|
198
|
-
catch (e) {
|
|
199
|
-
console.error('Error focusing element:', e);
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
// if focus was moved successfully and we want to activate keyboard navigation,
|
|
204
|
-
// dispatch a dummy keypress to ensure keyboard handlers are activated
|
|
205
|
-
if (success && activateKeyboardNav && document.activeElement !== prevFocusedElement) {
|
|
206
|
-
try {
|
|
207
|
-
// dispatch a no-op keydown event to activate any keyboard handlers
|
|
208
|
-
document.activeElement?.dispatchEvent(new KeyboardEvent('keydown', {
|
|
209
|
-
bubbles: true,
|
|
210
|
-
cancelable: true,
|
|
211
|
-
key: 'Tab',
|
|
212
|
-
code: 'Tab'
|
|
213
|
-
}));
|
|
214
|
-
}
|
|
215
|
-
catch (e) {
|
|
216
|
-
console.error('Error dispatching keyboard event:', e);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return success;
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Get all tabbable candidates in a container
|
|
79
|
+
* Collect the tabbable elements inside a container, in DOM order, skipping hidden ones.
|
|
223
80
|
*/
|
|
224
81
|
function getTabbableCandidates(container) {
|
|
225
|
-
if (!container || !container.querySelectorAll)
|
|
82
|
+
if (!container || !container.querySelectorAll) {
|
|
226
83
|
return [];
|
|
84
|
+
}
|
|
227
85
|
const TABBABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), ' +
|
|
228
86
|
'select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), ' +
|
|
229
87
|
'[contenteditable="true"]:not([tabindex="-1"])';
|
|
230
|
-
// use querySelector for better browser support
|
|
231
88
|
const elements = Array.from(container.querySelectorAll(TABBABLE_SELECTOR));
|
|
232
|
-
// filter out elements that are hidden, have display:none, etc.
|
|
233
89
|
return elements.filter((element) => {
|
|
234
|
-
if (element.tabIndex < 0)
|
|
90
|
+
if (element.tabIndex < 0) {
|
|
235
91
|
return false;
|
|
236
|
-
|
|
92
|
+
}
|
|
93
|
+
if (element.hasAttribute('disabled')) {
|
|
237
94
|
return false;
|
|
238
|
-
|
|
95
|
+
}
|
|
96
|
+
if (element.getAttribute('aria-hidden') === 'true') {
|
|
239
97
|
return false;
|
|
240
|
-
|
|
98
|
+
}
|
|
241
99
|
let current = element;
|
|
242
100
|
while (current) {
|
|
243
101
|
const style = window.getComputedStyle(current);
|
|
244
|
-
if (style.display === 'none' || style.visibility === 'hidden'
|
|
102
|
+
if (style.display === 'none' || style.visibility === 'hidden') {
|
|
245
103
|
return false;
|
|
246
104
|
}
|
|
247
105
|
current = current.parentElement;
|
|
@@ -250,1540 +108,1189 @@ function getTabbableCandidates(container) {
|
|
|
250
108
|
});
|
|
251
109
|
}
|
|
252
110
|
/**
|
|
253
|
-
*
|
|
111
|
+
* Focus the first focusable candidate, returning whether focus moved.
|
|
254
112
|
*/
|
|
255
|
-
function
|
|
256
|
-
const
|
|
257
|
-
candidates.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
candidate.
|
|
113
|
+
function focusFirst(candidates, preventScroll = false) {
|
|
114
|
+
const previouslyFocused = document.activeElement;
|
|
115
|
+
return candidates.some((candidate) => {
|
|
116
|
+
if (candidate === previouslyFocused) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
candidate.focus({ preventScroll });
|
|
120
|
+
return document.activeElement !== previouslyFocused;
|
|
262
121
|
});
|
|
263
|
-
// Return restore function
|
|
264
|
-
return () => {
|
|
265
|
-
candidates.forEach((candidate) => {
|
|
266
|
-
const originalValue = originalValues.get(candidate);
|
|
267
|
-
if (originalValue == null) {
|
|
268
|
-
candidate.removeAttribute('tabindex');
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
candidate.setAttribute('tabindex', originalValue);
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
};
|
|
275
122
|
}
|
|
276
123
|
/**
|
|
277
|
-
*
|
|
124
|
+
* Derive a slide direction (e.g. `"left"`, `"right up"`) from the relative position of two triggers.
|
|
125
|
+
* Used by the viewport to animate content as the active item changes.
|
|
126
|
+
*/
|
|
127
|
+
function getActivationDirection(previous, next) {
|
|
128
|
+
const previousCenter = getCenter(previous.getBoundingClientRect());
|
|
129
|
+
const nextCenter = getCenter(next.getBoundingClientRect());
|
|
130
|
+
const directions = [];
|
|
131
|
+
if (nextCenter.x < previousCenter.x) {
|
|
132
|
+
directions.push('left');
|
|
133
|
+
}
|
|
134
|
+
else if (nextCenter.x > previousCenter.x) {
|
|
135
|
+
directions.push('right');
|
|
136
|
+
}
|
|
137
|
+
if (nextCenter.y < previousCenter.y) {
|
|
138
|
+
directions.push('up');
|
|
139
|
+
}
|
|
140
|
+
else if (nextCenter.y > previousCenter.y) {
|
|
141
|
+
directions.push('down');
|
|
142
|
+
}
|
|
143
|
+
return directions.join(' ') || undefined;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Remove `id` from an element and all its descendants (used when cloning content for an exit
|
|
147
|
+
* animation, to avoid duplicate ids in the document).
|
|
278
148
|
*/
|
|
279
|
-
function
|
|
280
|
-
|
|
149
|
+
function removeIds(element) {
|
|
150
|
+
element.removeAttribute('id');
|
|
151
|
+
element.querySelectorAll('[id]').forEach((child) => child.removeAttribute('id'));
|
|
152
|
+
}
|
|
153
|
+
function getCenter(rect) {
|
|
154
|
+
return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
281
155
|
}
|
|
282
156
|
|
|
283
|
-
|
|
157
|
+
/**
|
|
158
|
+
* A single navigation menu item. Holds a trigger + content pair, or a standalone link.
|
|
159
|
+
*/
|
|
160
|
+
class RdxNavigationMenuItem {
|
|
284
161
|
constructor() {
|
|
285
|
-
this.
|
|
286
|
-
this.context = injectNavigationMenu();
|
|
287
|
-
this.value = input('', ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
162
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
288
163
|
/**
|
|
289
|
-
*
|
|
164
|
+
* A unique value that identifies the item. Falls back to a generated id.
|
|
290
165
|
*/
|
|
291
|
-
this.
|
|
292
|
-
|
|
293
|
-
this.
|
|
294
|
-
|
|
295
|
-
this.
|
|
296
|
-
this.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Handle keyboard entry into content from trigger
|
|
303
|
-
*/
|
|
304
|
-
onEntryKeyDown() {
|
|
305
|
-
// Check if we're using a viewport in a root menu
|
|
306
|
-
if (isRootNavigationMenu(this.context) && this.context.viewport && this.context.viewport()) {
|
|
307
|
-
const viewport = this.context.viewport();
|
|
308
|
-
if (viewport) {
|
|
309
|
-
// find tabbable elements in the viewport
|
|
310
|
-
const candidates = getTabbableCandidates(viewport);
|
|
311
|
-
if (candidates.length) {
|
|
312
|
-
this.ensureTabOrder();
|
|
313
|
-
// focus the first element
|
|
314
|
-
focusFirst(candidates);
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
// fallback to content if no viewport or no tabbable elements in viewport
|
|
320
|
-
if (this.contentRef()) {
|
|
321
|
-
// restore tab order if needed
|
|
322
|
-
const restoreFn = this._restoreContentTabOrderRef();
|
|
323
|
-
if (restoreFn)
|
|
324
|
-
restoreFn();
|
|
325
|
-
// find and focus first tabbable element
|
|
326
|
-
const candidates = getTabbableCandidates(this.contentRef());
|
|
327
|
-
if (candidates.length) {
|
|
328
|
-
focusFirst(candidates);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
focus() {
|
|
333
|
-
this.triggerOrLink()?.focus();
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Ensure elements are in the tab order by restoring any previously removed tabindex values
|
|
337
|
-
*/
|
|
338
|
-
ensureTabOrder() {
|
|
339
|
-
const restoreFn = this._restoreContentTabOrderRef();
|
|
340
|
-
if (restoreFn) {
|
|
341
|
-
restoreFn();
|
|
342
|
-
this._restoreContentTabOrderRef.set(null);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Handle focus coming from the focus proxy element
|
|
347
|
-
* @param side Which side the focus is coming from (start = from trigger, end = from after content)
|
|
348
|
-
*/
|
|
349
|
-
onFocusProxyEnter(side = 'start') {
|
|
350
|
-
// check for viewport first
|
|
351
|
-
if (isRootNavigationMenu(this.context) && this.context.viewport && this.context.viewport()) {
|
|
352
|
-
const viewport = this.context.viewport();
|
|
353
|
-
if (viewport) {
|
|
354
|
-
const candidates = getTabbableCandidates(viewport);
|
|
355
|
-
if (candidates.length) {
|
|
356
|
-
this.ensureTabOrder();
|
|
357
|
-
// focus first or last element depending on direction
|
|
358
|
-
focusFirst(side === 'start' ? candidates : [...candidates].reverse());
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
// fallback to content
|
|
364
|
-
if (this.contentRef()) {
|
|
365
|
-
// restore tab order if needed
|
|
366
|
-
const restoreFn = this._restoreContentTabOrderRef();
|
|
367
|
-
if (restoreFn)
|
|
368
|
-
restoreFn();
|
|
369
|
-
// find and focus appropriate element based on direction
|
|
370
|
-
const candidates = getTabbableCandidates(this.contentRef());
|
|
371
|
-
if (candidates.length) {
|
|
372
|
-
// Focus first or last element depending on which direction we're coming from
|
|
373
|
-
focusFirst(side === 'start' ? candidates : [...candidates].reverse());
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* Handle focus moving outside of the content
|
|
379
|
-
* Remove elements from tab order when not focused
|
|
380
|
-
*/
|
|
381
|
-
onContentFocusOutside() {
|
|
382
|
-
// get all tabbable elements from both viewport and content
|
|
383
|
-
let allCandidates = [];
|
|
384
|
-
// check viewport first
|
|
385
|
-
if (isRootNavigationMenu(this.context) && this.context.viewport && this.context.viewport()) {
|
|
386
|
-
const viewport = this.context.viewport();
|
|
387
|
-
if (viewport) {
|
|
388
|
-
allCandidates = getTabbableCandidates(viewport);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
// ... also check direct content
|
|
392
|
-
if (this.contentRef()) {
|
|
393
|
-
const contentCandidates = getTabbableCandidates(this.contentRef());
|
|
394
|
-
allCandidates = [...allCandidates, ...contentCandidates];
|
|
395
|
-
}
|
|
396
|
-
// remove from tab order and store restore function
|
|
397
|
-
if (allCandidates.length) {
|
|
398
|
-
this._restoreContentTabOrderRef.set(removeFromTabOrder(allCandidates));
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Handle content being closed from root menu
|
|
403
|
-
*/
|
|
404
|
-
onRootContentClose() {
|
|
405
|
-
this.onContentFocusOutside();
|
|
406
|
-
}
|
|
407
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxNavigationMenuItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
408
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.3", type: RdxNavigationMenuItemDirective, isStandalone: true, selector: "[rdxNavigationMenuItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.value": "value()" } }, queries: [{ propertyName: "triggerOrLink", first: true, predicate: RdxNavigationMenuFocusableOption, descendants: true, isSignal: true }], exportAs: ["rdxNavigationMenuItem"], ngImport: i0 }); }
|
|
166
|
+
this.value = input('', { ...(ngDevMode ? { debugName: "value" } : /* istanbul ignore next */ {}), transform: (value) => value || `item-${generateId()}` });
|
|
167
|
+
/** The trigger element, set by the trigger directive. */
|
|
168
|
+
this.triggerRef = signal(null, ...(ngDevMode ? [{ debugName: "triggerRef" }] : /* istanbul ignore next */ []));
|
|
169
|
+
/** Whether this item is the currently open one. */
|
|
170
|
+
this.isOpen = computed(() => this.rootContext.value() === this.value(), ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
171
|
+
this.triggerId = computed(() => this.rootContext.triggerId(this.value()), ...(ngDevMode ? [{ debugName: "triggerId" }] : /* istanbul ignore next */ []));
|
|
172
|
+
this.contentId = computed(() => this.rootContext.contentId(this.value()), ...(ngDevMode ? [{ debugName: "contentId" }] : /* istanbul ignore next */ []));
|
|
173
|
+
}
|
|
174
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
175
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNavigationMenuItem, isStandalone: true, selector: "[rdxNavigationMenuItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["rdxNavigationMenuItem"], ngImport: i0 }); }
|
|
409
176
|
}
|
|
410
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
177
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuItem, decorators: [{
|
|
411
178
|
type: Directive,
|
|
412
179
|
args: [{
|
|
413
180
|
selector: '[rdxNavigationMenuItem]',
|
|
414
|
-
host: {
|
|
415
|
-
'[attr.value]': 'value()'
|
|
416
|
-
},
|
|
417
181
|
exportAs: 'rdxNavigationMenuItem'
|
|
418
182
|
}]
|
|
419
|
-
}] });
|
|
183
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }] } });
|
|
420
184
|
|
|
421
|
-
|
|
185
|
+
/**
|
|
186
|
+
* The content shown when its item is open. Used as a structural directive; its template is rendered
|
|
187
|
+
* into the shared {@link RdxNavigationMenuViewport}.
|
|
188
|
+
*
|
|
189
|
+
* ```html
|
|
190
|
+
* <ng-container *rdxNavigationMenuContent>…</ng-container>
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
class RdxNavigationMenuContent {
|
|
422
194
|
constructor() {
|
|
423
|
-
this.
|
|
424
|
-
this.
|
|
425
|
-
this.
|
|
426
|
-
this.document = injectDocument();
|
|
427
|
-
this.item = inject(RdxNavigationMenuItemDirective);
|
|
428
|
-
this.context = injectNavigationMenu();
|
|
195
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
196
|
+
this.item = inject(RdxNavigationMenuItem);
|
|
197
|
+
this.templateRef = inject(TemplateRef);
|
|
429
198
|
/**
|
|
430
|
-
*
|
|
431
|
-
* Useful for animations or SEO.
|
|
432
|
-
* @default false
|
|
199
|
+
* Required by the structural directive syntax; the value is unused.
|
|
433
200
|
*/
|
|
434
|
-
this.
|
|
435
|
-
/**
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
this.
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
templateRef: this.template,
|
|
452
|
-
forceMount: this.forceMount(),
|
|
453
|
-
value: this.item.value(),
|
|
454
|
-
getMotionAttribute: this.getMotionAttribute.bind(this),
|
|
455
|
-
additionalAttrs: {
|
|
456
|
-
id: this.contentId,
|
|
457
|
-
'aria-labelledby': this.triggerId,
|
|
458
|
-
role: 'menu'
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
// add Escape key handler
|
|
463
|
-
this.escapeHandler = (event) => {
|
|
464
|
-
if (event.key === ESCAPE && this.context.value() === this.item.value()) {
|
|
465
|
-
// mark that this close was triggered by Escape
|
|
466
|
-
this.item.wasEscapeCloseRef.set(true);
|
|
467
|
-
// close the content
|
|
468
|
-
if (this.context.onItemDismiss) {
|
|
469
|
-
this.context.onItemDismiss();
|
|
470
|
-
}
|
|
471
|
-
// refocus the trigger
|
|
472
|
-
setTimeout(() => {
|
|
473
|
-
const trigger = this.item.triggerRef();
|
|
474
|
-
if (trigger)
|
|
475
|
-
trigger.focus();
|
|
476
|
-
}, 0);
|
|
477
|
-
event.preventDefault();
|
|
478
|
-
event.stopPropagation();
|
|
479
|
-
}
|
|
480
|
-
};
|
|
481
|
-
this.ngZone.runOutsideAngular(() => {
|
|
482
|
-
if (this.escapeHandler) {
|
|
483
|
-
this.document.addEventListener('keydown', this.escapeHandler);
|
|
484
|
-
}
|
|
201
|
+
this.rdxNavigationMenuContent = input(false, { ...(ngDevMode ? { debugName: "rdxNavigationMenuContent" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
202
|
+
/**
|
|
203
|
+
* Keep the content mounted in the viewport even when its item is closed.
|
|
204
|
+
*/
|
|
205
|
+
this.forceMount = input(false, { ...(ngDevMode ? { debugName: "forceMount" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
206
|
+
effect((onCleanup) => {
|
|
207
|
+
const value = this.item.value();
|
|
208
|
+
// Register untracked so reading/writing the root's `contents` map inside registerContent
|
|
209
|
+
// doesn't make this effect re-run when other items register.
|
|
210
|
+
const unregister = untracked(() => this.rootContext.registerContent({
|
|
211
|
+
value,
|
|
212
|
+
contentId: this.rootContext.contentId(value),
|
|
213
|
+
triggerId: this.rootContext.triggerId(value),
|
|
214
|
+
templateRef: this.templateRef,
|
|
215
|
+
forceMount: this.forceMount
|
|
216
|
+
}));
|
|
217
|
+
onCleanup(unregister);
|
|
485
218
|
});
|
|
486
219
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
// unregister from viewport
|
|
490
|
-
if (isRootNavigationMenu(this.context) && this.context.onViewportContentRemove) {
|
|
491
|
-
this.context.onViewportContentRemove(this.item.value());
|
|
492
|
-
}
|
|
493
|
-
// remove escape key handler
|
|
494
|
-
if (this.escapeHandler) {
|
|
495
|
-
this.document.removeEventListener('keydown', this.escapeHandler);
|
|
496
|
-
this.escapeHandler = null;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
/** @ignore - Compute motion attribute for animations */
|
|
500
|
-
getMotionAttribute() {
|
|
501
|
-
if (!isRootNavigationMenu(this.context))
|
|
502
|
-
return null;
|
|
503
|
-
const itemValues = Array.from(this.context.viewportContent?.() ?? new Map()).map(([value]) => value);
|
|
504
|
-
return getMotionAttribute(this.context.value(), this.context.previousValue(), this.item.value(), itemValues, this.context.dir);
|
|
505
|
-
}
|
|
506
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxNavigationMenuContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
507
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.3", type: RdxNavigationMenuContentDirective, isStandalone: true, selector: "[rdxNavigationMenuContent]", inputs: { rdxNavigationMenuContent: { classPropertyName: "rdxNavigationMenuContent", publicName: "rdxNavigationMenuContent", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, forceMount: { classPropertyName: "forceMount", publicName: "forceMount", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
220
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
221
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNavigationMenuContent, isStandalone: true, selector: "[rdxNavigationMenuContent]", inputs: { rdxNavigationMenuContent: { classPropertyName: "rdxNavigationMenuContent", publicName: "rdxNavigationMenuContent", isSignal: true, isRequired: false, transformFunction: null }, forceMount: { classPropertyName: "forceMount", publicName: "forceMount", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
508
222
|
}
|
|
509
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
223
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuContent, decorators: [{
|
|
510
224
|
type: Directive,
|
|
511
225
|
args: [{
|
|
512
226
|
selector: '[rdxNavigationMenuContent]'
|
|
513
227
|
}]
|
|
514
|
-
}], propDecorators: { rdxNavigationMenuContent: [{
|
|
515
|
-
type: Input,
|
|
516
|
-
args: [{ transform: booleanAttribute }]
|
|
517
|
-
}] } });
|
|
228
|
+
}], ctorParameters: () => [], propDecorators: { rdxNavigationMenuContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "rdxNavigationMenuContent", required: false }] }], forceMount: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceMount", required: false }] }] } });
|
|
518
229
|
|
|
519
|
-
|
|
230
|
+
/**
|
|
231
|
+
* A visual indicator (e.g. a caret) rendered inside a trigger. Exposes the open state so the icon
|
|
232
|
+
* can rotate when its item opens.
|
|
233
|
+
*/
|
|
234
|
+
class RdxNavigationMenuIcon {
|
|
520
235
|
constructor() {
|
|
521
|
-
this.
|
|
522
|
-
this.
|
|
523
|
-
this.renderer = inject(Renderer2);
|
|
524
|
-
/**
|
|
525
|
-
* Used to keep the indicator rendered and available in the DOM, even when hidden.
|
|
526
|
-
* Useful for animations.
|
|
527
|
-
* @default false
|
|
528
|
-
*/
|
|
529
|
-
this.forceMount = input(false, ...(ngDevMode ? [{ debugName: "forceMount", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
530
|
-
/** @ignore */
|
|
531
|
-
this._position = signal(null, ...(ngDevMode ? [{ debugName: "_position" }] : []));
|
|
532
|
-
/** @ignore */
|
|
533
|
-
this._activeTrigger = signal(null, ...(ngDevMode ? [{ debugName: "_activeTrigger" }] : []));
|
|
534
|
-
/** @ignore */
|
|
535
|
-
this._resizeObserver = new ResizeObserver(() => this.updatePosition());
|
|
536
|
-
this.isVisible = computed(() => Boolean(this.context.value() || this.forceMount()), ...(ngDevMode ? [{ debugName: "isVisible" }] : []));
|
|
537
|
-
// set up effect for tracking active trigger and position
|
|
538
|
-
effect(() => {
|
|
539
|
-
// this effect runs when the current value changes
|
|
540
|
-
const value = this.context.value();
|
|
541
|
-
untracked(() => {
|
|
542
|
-
if (value && isRootNavigationMenu(this.context)) {
|
|
543
|
-
this.findAndSetActiveTrigger();
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
});
|
|
547
|
-
// initialize observers for position tracking
|
|
548
|
-
runInInjectionContext(this.context, () => {
|
|
549
|
-
if (isRootNavigationMenu(this.context) && this.context.indicatorTrack) {
|
|
550
|
-
const track = this.context.indicatorTrack();
|
|
551
|
-
if (track) {
|
|
552
|
-
// observe size changes on the track
|
|
553
|
-
this._resizeObserver.observe(track);
|
|
554
|
-
}
|
|
555
|
-
// initial position update if menu is open
|
|
556
|
-
if (this.context.value()) {
|
|
557
|
-
setTimeout(() => this.findAndSetActiveTrigger(), 0);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
/** @ignore */
|
|
563
|
-
ngOnDestroy() {
|
|
564
|
-
this._resizeObserver.disconnect();
|
|
565
|
-
}
|
|
566
|
-
/** @ignore */
|
|
567
|
-
findAndSetActiveTrigger() {
|
|
568
|
-
if (!isRootNavigationMenu(this.context) || !this.context.indicatorTrack)
|
|
569
|
-
return;
|
|
570
|
-
const track = this.context.indicatorTrack();
|
|
571
|
-
if (!track)
|
|
572
|
-
return;
|
|
573
|
-
// find all triggers within the track
|
|
574
|
-
const triggers = Array.from(track.querySelectorAll('[rdxNavigationMenuTrigger]'));
|
|
575
|
-
// find the active trigger based on the current menu value
|
|
576
|
-
const activeTrigger = triggers.find((trigger) => {
|
|
577
|
-
const item = trigger.closest('[rdxNavigationMenuItem]');
|
|
578
|
-
if (!item)
|
|
579
|
-
return false;
|
|
580
|
-
const value = item.getAttribute('value');
|
|
581
|
-
return value === this.context.value();
|
|
582
|
-
});
|
|
583
|
-
if (activeTrigger && activeTrigger !== this._activeTrigger()) {
|
|
584
|
-
this._activeTrigger.set(activeTrigger);
|
|
585
|
-
this.updatePosition();
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
/** @ignore */
|
|
589
|
-
updatePosition() {
|
|
590
|
-
const trigger = this._activeTrigger();
|
|
591
|
-
if (!trigger)
|
|
592
|
-
return;
|
|
593
|
-
const isHorizontal = this.context.orientation === 'horizontal';
|
|
594
|
-
// calculate new position
|
|
595
|
-
const newPosition = {
|
|
596
|
-
size: isHorizontal ? trigger.offsetWidth : trigger.offsetHeight,
|
|
597
|
-
offset: isHorizontal ? trigger.offsetLeft : trigger.offsetTop
|
|
598
|
-
};
|
|
599
|
-
// only update if position has changed
|
|
600
|
-
if (JSON.stringify(newPosition) !== JSON.stringify(this._position())) {
|
|
601
|
-
this._position.set(newPosition);
|
|
602
|
-
// apply position styles
|
|
603
|
-
const styles = isHorizontal
|
|
604
|
-
? {
|
|
605
|
-
position: 'absolute',
|
|
606
|
-
left: '0',
|
|
607
|
-
width: `${newPosition.size}px`,
|
|
608
|
-
transform: `translateX(${newPosition.offset}px)`
|
|
609
|
-
}
|
|
610
|
-
: {
|
|
611
|
-
position: 'absolute',
|
|
612
|
-
top: '0',
|
|
613
|
-
height: `${newPosition.size}px`,
|
|
614
|
-
transform: `translateY(${newPosition.offset}px)`
|
|
615
|
-
};
|
|
616
|
-
Object.entries(styles).forEach(([key, value]) => {
|
|
617
|
-
this.renderer.setStyle(this.elementRef.nativeElement, key, value);
|
|
618
|
-
});
|
|
619
|
-
}
|
|
236
|
+
this.item = inject(RdxNavigationMenuItem);
|
|
237
|
+
this.open = computed(() => this.item.isOpen(), ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
620
238
|
}
|
|
621
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
622
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "
|
|
239
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
240
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNavigationMenuIcon, isStandalone: true, selector: "[rdxNavigationMenuIcon]", host: { attributes: { "aria-hidden": "true" }, properties: { "attr.data-state": "open() ? \"open\" : \"closed\"" } }, ngImport: i0 }); }
|
|
623
241
|
}
|
|
624
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
242
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuIcon, decorators: [{
|
|
625
243
|
type: Directive,
|
|
626
244
|
args: [{
|
|
627
|
-
selector: '[
|
|
245
|
+
selector: '[rdxNavigationMenuIcon]',
|
|
628
246
|
host: {
|
|
629
|
-
'
|
|
630
|
-
'[attr.data-
|
|
631
|
-
'[style.display]': 'isVisible() ? null : "none"',
|
|
632
|
-
'aria-hidden': 'true'
|
|
247
|
+
'aria-hidden': 'true',
|
|
248
|
+
'[attr.data-state]': 'open() ? "open" : "closed"'
|
|
633
249
|
}
|
|
634
250
|
}]
|
|
635
|
-
}]
|
|
251
|
+
}] });
|
|
636
252
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
253
|
+
/**
|
|
254
|
+
* A navigation link. Closes the menu on selection unless prevented.
|
|
255
|
+
*
|
|
256
|
+
* Used both as a top-level menubar item and inside content. It is a plain tabbable anchor (not part
|
|
257
|
+
* of the menubar's arrow-key roving), matching Base UI.
|
|
258
|
+
*/
|
|
259
|
+
class RdxNavigationMenuLink {
|
|
640
260
|
constructor() {
|
|
641
|
-
|
|
642
|
-
this.rovingFocusItem = inject(RdxRovingFocusItemDirective, { self: true });
|
|
643
|
-
this.uniqueId = generateId();
|
|
644
|
-
this.active = input(false, ...(ngDevMode ? [{ debugName: "active", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
645
|
-
this.onSelect = input(...(ngDevMode ? [undefined, { debugName: "onSelect" }] : []));
|
|
261
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
646
262
|
this.elementRef = inject(ElementRef);
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Whether the link represents the current page.
|
|
265
|
+
*/
|
|
266
|
+
this.active = input(false, { ...(ngDevMode ? { debugName: "active" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
267
|
+
/**
|
|
268
|
+
* Whether selecting the link should close the menu.
|
|
269
|
+
*/
|
|
270
|
+
this.closeOnClick = input(true, { ...(ngDevMode ? { debugName: "closeOnClick" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
271
|
+
/**
|
|
272
|
+
* Emits when the link is selected. Call `preventDefault()` to keep the menu open.
|
|
273
|
+
*/
|
|
274
|
+
this.onSelect = output();
|
|
653
275
|
}
|
|
654
276
|
onClick(event) {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
bubbles: true,
|
|
659
|
-
cancelable: true
|
|
660
|
-
});
|
|
661
|
-
// add one-time listener for onSelect handler
|
|
662
|
-
const onSelect = this.onSelect();
|
|
663
|
-
if (onSelect) {
|
|
664
|
-
target.addEventListener(LINK_SELECT, onSelect, { once: true });
|
|
665
|
-
}
|
|
666
|
-
// dispatch event
|
|
667
|
-
target.dispatchEvent(linkSelectEvent);
|
|
668
|
-
// if not prevented and not meta key, dismiss content
|
|
669
|
-
if (!linkSelectEvent.defaultPrevented && !event.metaKey) {
|
|
670
|
-
const dismissEvent = new CustomEvent(ROOT_CONTENT_DISMISS, {
|
|
671
|
-
bubbles: true,
|
|
672
|
-
cancelable: true
|
|
673
|
-
});
|
|
674
|
-
target.dispatchEvent(dismissEvent);
|
|
277
|
+
this.onSelect.emit(event);
|
|
278
|
+
if (this.closeOnClick() && !event.defaultPrevented) {
|
|
279
|
+
this.rootContext.close('link-select', event);
|
|
675
280
|
}
|
|
676
281
|
}
|
|
677
282
|
onKeydown(event) {
|
|
678
|
-
// activate link on Enter or Space
|
|
679
283
|
if (event.key === ENTER || event.key === SPACE) {
|
|
680
|
-
// prevent default behavior like scrolling (Space) or form submission (Enter) BEFORE simulating the click.
|
|
681
284
|
event.preventDefault();
|
|
682
|
-
|
|
683
|
-
const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true });
|
|
684
|
-
this.elementRef.nativeElement.dispatchEvent(clickEvent);
|
|
685
|
-
return;
|
|
285
|
+
this.elementRef.nativeElement.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
686
286
|
}
|
|
687
287
|
}
|
|
688
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
689
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
288
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuLink, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
289
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNavigationMenuLink, isStandalone: true, selector: "[rdxNavigationMenuLink]", inputs: { active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, closeOnClick: { classPropertyName: "closeOnClick", publicName: "closeOnClick", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect" }, host: { listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)" }, properties: { "attr.data-active": "active() ? \"\" : undefined", "attr.aria-current": "active() ? \"page\" : undefined" } }, ngImport: i0 }); }
|
|
690
290
|
}
|
|
691
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
291
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuLink, decorators: [{
|
|
692
292
|
type: Directive,
|
|
693
293
|
args: [{
|
|
694
294
|
selector: '[rdxNavigationMenuLink]',
|
|
695
|
-
hostDirectives: [{ directive: RdxRovingFocusItemDirective, inputs: ['focusable'] }],
|
|
696
295
|
host: {
|
|
697
296
|
'[attr.data-active]': 'active() ? "" : undefined',
|
|
698
297
|
'[attr.aria-current]': 'active() ? "page" : undefined',
|
|
699
298
|
'(click)': 'onClick($event)',
|
|
700
299
|
'(keydown)': 'onKeydown($event)'
|
|
701
|
-
}
|
|
702
|
-
providers: [{ provide: RdxNavigationMenuFocusableOption, useExisting: RdxNavigationMenuLinkDirective }]
|
|
300
|
+
}
|
|
703
301
|
}]
|
|
704
|
-
}] });
|
|
302
|
+
}], propDecorators: { active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], closeOnClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnClick", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }] } });
|
|
705
303
|
|
|
706
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Contains the navigation menu items. Renders as a menubar with roving keyboard focus.
|
|
306
|
+
*/
|
|
307
|
+
class RdxNavigationMenuList {
|
|
707
308
|
constructor() {
|
|
708
|
-
this.
|
|
709
|
-
this.elementRef = inject((ElementRef));
|
|
710
|
-
this.renderer = inject(Renderer2);
|
|
309
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
711
310
|
this.rovingFocusGroup = inject(RdxRovingFocusGroupDirective, { self: true });
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
718
|
-
/**
|
|
719
|
-
* @ignore
|
|
720
|
-
*/
|
|
721
|
-
ngAfterContentInit() {
|
|
722
|
-
const items = this.items();
|
|
723
|
-
this.keyManager = new FocusKeyManager(items);
|
|
724
|
-
if (this.context.orientation === 'horizontal') {
|
|
725
|
-
this.keyManager.withHorizontalOrientation(this.context.dir || 'ltr');
|
|
726
|
-
}
|
|
727
|
-
else {
|
|
728
|
-
this.keyManager.withVerticalOrientation();
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
/**
|
|
732
|
-
* @ignore
|
|
733
|
-
*/
|
|
734
|
-
ngAfterViewInit() {
|
|
735
|
-
this.rovingFocusGroup.setOrientation(this.context.orientation);
|
|
736
|
-
this.rovingFocusGroup.setDir(this.context.dir);
|
|
737
|
-
// looping typically only applies to the root menu bar
|
|
738
|
-
if (isRootNavigationMenu(this.context)) {
|
|
739
|
-
this.rovingFocusGroup.setLoop(this.context.loop ?? false);
|
|
740
|
-
}
|
|
741
|
-
else {
|
|
742
|
-
this.rovingFocusGroup.setLoop(false);
|
|
743
|
-
}
|
|
744
|
-
if (isRootNavigationMenu(this.context) && this.context.onIndicatorTrackChange) {
|
|
745
|
-
const listElement = this.elementRef.nativeElement;
|
|
746
|
-
const parent = listElement.parentNode;
|
|
747
|
-
// ensure parent exists and list hasn't already been wrapped
|
|
748
|
-
if (parent && !listElement.parentElement?.hasAttribute('data-radix-navigation-menu-list-wrapper')) {
|
|
749
|
-
// create a wrapper div with relative positioning
|
|
750
|
-
const wrapper = this.renderer.createElement('div');
|
|
751
|
-
this.renderer.setAttribute(wrapper, 'data-radix-navigation-menu-list-wrapper', ''); // Add marker
|
|
752
|
-
this.renderer.setStyle(wrapper, 'position', 'relative');
|
|
753
|
-
// insert the wrapper before the list element in the parent
|
|
754
|
-
this.renderer.insertBefore(parent, wrapper, listElement);
|
|
755
|
-
// move the list element inside the new wrapper
|
|
756
|
-
this.renderer.appendChild(wrapper, listElement);
|
|
757
|
-
// register the wrapper element as the track for the indicator positioning
|
|
758
|
-
this.context.onIndicatorTrackChange(wrapper);
|
|
759
|
-
}
|
|
760
|
-
else if (listElement.parentElement?.hasAttribute('data-radix-navigation-menu-list-wrapper')) {
|
|
761
|
-
// if wrapper somehow already exists, ensure context has the correct reference
|
|
762
|
-
this.context.onIndicatorTrackChange(listElement.parentElement);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
311
|
+
effect(() => {
|
|
312
|
+
this.rovingFocusGroup.setOrientation(this.rootContext.orientation());
|
|
313
|
+
this.rovingFocusGroup.setDir(this.rootContext.dir());
|
|
314
|
+
this.rovingFocusGroup.setLoop(this.rootContext.loop());
|
|
315
|
+
});
|
|
765
316
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
onKeydown(event) {
|
|
770
|
-
if (!this.keyManager.activeItem) {
|
|
771
|
-
this.keyManager.setFirstItemActive();
|
|
772
|
-
}
|
|
773
|
-
if (event.key === TAB && event.shiftKey) {
|
|
774
|
-
if (this.keyManager.activeItemIndex === 0)
|
|
775
|
-
return;
|
|
776
|
-
this.keyManager.setPreviousItemActive();
|
|
777
|
-
event.preventDefault();
|
|
778
|
-
}
|
|
779
|
-
else if (event.key === TAB) {
|
|
780
|
-
const items = this.items();
|
|
781
|
-
if (this.keyManager.activeItemIndex === items.length - 1) {
|
|
782
|
-
return;
|
|
783
|
-
}
|
|
784
|
-
this.keyManager.setNextItemActive();
|
|
785
|
-
event.preventDefault();
|
|
786
|
-
}
|
|
787
|
-
else {
|
|
788
|
-
this.keyManager.onKeydown(event);
|
|
317
|
+
onPointerLeave(event) {
|
|
318
|
+
if (event.pointerType === 'touch') {
|
|
319
|
+
return;
|
|
789
320
|
}
|
|
321
|
+
this.rootContext.closeOnHover();
|
|
790
322
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
*/
|
|
794
|
-
setActiveItem(item) {
|
|
795
|
-
this.keyManager.setActiveItem(item);
|
|
796
|
-
}
|
|
797
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxNavigationMenuListDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
798
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.3", type: RdxNavigationMenuListDirective, isStandalone: true, selector: "[rdxNavigationMenuList]", host: { attributes: { "role": "menubar" }, listeners: { "keydown": "onKeydown($event)" } }, queries: [{ propertyName: "items", predicate: i0.forwardRef(() => RdxNavigationMenuItemDirective), descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.RdxRovingFocusGroupDirective }], ngImport: i0 }); }
|
|
323
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuList, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
324
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNavigationMenuList, isStandalone: true, selector: "[rdxNavigationMenuList]", host: { attributes: { "role": "menubar" }, listeners: { "pointerleave": "onPointerLeave($event)" }, properties: { "attr.data-orientation": "rootContext.orientation()" } }, hostDirectives: [{ directive: i1$1.RdxRovingFocusGroupDirective }], ngImport: i0 }); }
|
|
799
325
|
}
|
|
800
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
326
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuList, decorators: [{
|
|
801
327
|
type: Directive,
|
|
802
328
|
args: [{
|
|
803
329
|
selector: '[rdxNavigationMenuList]',
|
|
804
330
|
hostDirectives: [RdxRovingFocusGroupDirective],
|
|
805
331
|
host: {
|
|
806
332
|
role: 'menubar',
|
|
807
|
-
'
|
|
333
|
+
'[attr.data-orientation]': 'rootContext.orientation()',
|
|
334
|
+
'(pointerleave)': 'onPointerLeave($event)'
|
|
808
335
|
}
|
|
809
336
|
}]
|
|
810
|
-
}] });
|
|
337
|
+
}], ctorParameters: () => [] });
|
|
811
338
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
RdxNavigationMenuAction["CLOSE"] = "close";
|
|
817
|
-
})(RdxNavigationMenuAction || (RdxNavigationMenuAction = {}));
|
|
818
|
-
class RdxNavigationMenuDirective {
|
|
819
|
-
// State
|
|
820
|
-
#value;
|
|
821
|
-
#previousValue;
|
|
822
|
-
#indicatorTrack;
|
|
823
|
-
#viewport;
|
|
824
|
-
#viewportContent;
|
|
825
|
-
#rootNavigationMenu;
|
|
826
|
-
#userDismissedByClick;
|
|
827
|
-
#isOpenDelayed;
|
|
828
|
-
// pointer tracking
|
|
829
|
-
#isPointerOverContent;
|
|
830
|
-
#isPointerOverTrigger;
|
|
339
|
+
/**
|
|
340
|
+
* The shared container for the active item's content.
|
|
341
|
+
*/
|
|
342
|
+
class RdxNavigationMenuPopup {
|
|
831
343
|
constructor() {
|
|
344
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
345
|
+
this.dismissableLayer = inject(RdxDismissableLayer);
|
|
346
|
+
this.wrapper = inject(RdxPopperContentWrapper, { optional: true });
|
|
347
|
+
this.layersContext = inject(RdxDismissableLayersContextToken);
|
|
832
348
|
this.elementRef = inject(ElementRef);
|
|
833
|
-
this.
|
|
834
|
-
this.
|
|
835
|
-
|
|
836
|
-
this
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
this
|
|
841
|
-
this
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
this.
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
this.
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
this.clickIgnoreDuration = 250;
|
|
861
|
-
this.delayDuration = 200;
|
|
862
|
-
this.skipDelayDuration = 300;
|
|
863
|
-
this.loop = false;
|
|
864
|
-
this.cssAnimation = false;
|
|
865
|
-
this.cssOpeningAnimation = false;
|
|
866
|
-
this.cssClosingAnimation = false;
|
|
867
|
-
this.isRootMenu = true;
|
|
868
|
-
this.cssAnimationStatus = signal(null, ...(ngDevMode ? [{ debugName: "cssAnimationStatus" }] : []));
|
|
869
|
-
// exposed state as functions for the token
|
|
870
|
-
this.value = () => this.#value();
|
|
871
|
-
this.previousValue = () => this.#previousValue();
|
|
872
|
-
this.rootNavigationMenu = () => this.#rootNavigationMenu();
|
|
873
|
-
this.indicatorTrack = () => this.#indicatorTrack();
|
|
874
|
-
this.viewport = () => this.#viewport();
|
|
875
|
-
this.viewportContent = () => this.#viewportContent();
|
|
876
|
-
// exposed pointer state
|
|
877
|
-
this.setTriggerPointerState = (isOver) => this.#isPointerOverTrigger.set(isOver);
|
|
878
|
-
this.setContentPointerState = (isOver) => this.#isPointerOverContent.set(isOver);
|
|
879
|
-
this.isPointerInSystem = () => this.#isPointerOverContent() || this.#isPointerOverTrigger();
|
|
880
|
-
// exposed animation state
|
|
881
|
-
this.getCssAnimation = () => this.cssAnimation;
|
|
882
|
-
this.getCssOpeningAnimation = () => this.cssOpeningAnimation;
|
|
883
|
-
this.getCssClosingAnimation = () => this.cssClosingAnimation;
|
|
349
|
+
this.side = computed(() => this.wrapper?.placedSide(), ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
350
|
+
this.align = computed(() => this.wrapper?.placedAlign(), ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
351
|
+
/** Names the menu after the active trigger so the `role="menu"` element has an accessible name. */
|
|
352
|
+
this.labelledBy = computed(() => {
|
|
353
|
+
const value = this.rootContext.value() ?? this.rootContext.previousValue();
|
|
354
|
+
return value ? this.rootContext.triggerId(value) : undefined;
|
|
355
|
+
}, ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
|
|
356
|
+
this.dismissReason = 'none';
|
|
357
|
+
this.dismissEvent = new Event('navigation-menu.dismiss');
|
|
358
|
+
/**
|
|
359
|
+
* Event handler called when the escape key is down. Can be prevented.
|
|
360
|
+
*/
|
|
361
|
+
this.escapeKeyDown = outputFromObservable(outputToObservable(this.dismissableLayer.escapeKeyDown));
|
|
362
|
+
/**
|
|
363
|
+
* Event handler called when a pointerdown event happens outside the popup. Can be prevented.
|
|
364
|
+
*/
|
|
365
|
+
this.pointerDownOutside = outputFromObservable(outputToObservable(this.dismissableLayer.pointerDownOutside));
|
|
366
|
+
/**
|
|
367
|
+
* Event handler called when focus moves outside the popup. Can be prevented.
|
|
368
|
+
*/
|
|
369
|
+
this.focusOutside = outputFromObservable(outputToObservable(this.dismissableLayer.focusOutside));
|
|
370
|
+
const destroyRef = inject(DestroyRef);
|
|
371
|
+
const unregisterTransitionElement = this.rootContext.registerTransitionElement(this.elementRef.nativeElement);
|
|
372
|
+
destroyRef.onDestroy(unregisterTransitionElement);
|
|
373
|
+
// Register the triggers as dismissable-layer branches so a pointer-down or (async) focus move
|
|
374
|
+
// onto a trigger counts as "inside" — otherwise focusing a sibling trigger to switch items,
|
|
375
|
+
// or returning focus to the trigger, would dismiss the menu. See dismissable-layer gotcha.
|
|
884
376
|
effect(() => {
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
}
|
|
892
|
-
else {
|
|
893
|
-
// menu is closed, start skip delay timer
|
|
894
|
-
this.window.clearTimeout(this.skipDelayTimerRef);
|
|
895
|
-
this.skipDelayTimerRef = this.window.setTimeout(() => {
|
|
896
|
-
this.#isOpenDelayed.set(true);
|
|
897
|
-
}, this.skipDelayDuration);
|
|
898
|
-
}
|
|
377
|
+
const triggers = this.rootContext.triggers();
|
|
378
|
+
untracked(() => this.layersContext.branches.update((branches) => {
|
|
379
|
+
const next = new Set(branches);
|
|
380
|
+
triggers.forEach((trigger) => next.add(trigger));
|
|
381
|
+
return [...next];
|
|
382
|
+
}));
|
|
899
383
|
});
|
|
900
|
-
|
|
901
|
-
.
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
384
|
+
destroyRef.onDestroy(() => {
|
|
385
|
+
const triggers = this.rootContext.triggers();
|
|
386
|
+
this.layersContext.branches.update((branches) => branches.filter((el) => !triggers.includes(el)));
|
|
387
|
+
});
|
|
388
|
+
this.dismissableLayer.escapeKeyDown.subscribe((event) => {
|
|
389
|
+
this.dismissReason = 'escape-key';
|
|
390
|
+
this.dismissEvent = event;
|
|
391
|
+
});
|
|
392
|
+
this.dismissableLayer.pointerDownOutside.subscribe((event) => {
|
|
393
|
+
this.dismissReason = 'outside-press';
|
|
394
|
+
this.dismissEvent = event;
|
|
395
|
+
});
|
|
396
|
+
this.dismissableLayer.focusOutside.subscribe((event) => {
|
|
397
|
+
this.dismissReason = 'focus-out';
|
|
398
|
+
this.dismissEvent = event;
|
|
399
|
+
});
|
|
400
|
+
this.dismissableLayer.dismiss.subscribe(() => {
|
|
401
|
+
const reason = this.dismissReason;
|
|
402
|
+
const event = this.dismissEvent;
|
|
403
|
+
this.dismissReason = 'none';
|
|
404
|
+
this.dismissEvent = new Event('navigation-menu.dismiss');
|
|
405
|
+
this.rootContext.close(reason, event);
|
|
406
|
+
// Return focus to the trigger after an Escape dismissal.
|
|
407
|
+
if (reason === 'escape-key') {
|
|
408
|
+
this.rootContext.trigger()?.focus();
|
|
918
409
|
}
|
|
919
|
-
})
|
|
920
|
-
.subscribe();
|
|
921
|
-
// set up document mouseleave handler to close menu when mouse leaves window
|
|
922
|
-
this.documentMouseLeaveHandler = () => this.handleClose();
|
|
923
|
-
this.document.addEventListener('mouseleave', this.documentMouseLeaveHandler);
|
|
410
|
+
});
|
|
924
411
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
this.window.clearTimeout(this.skipDelayTimerRef);
|
|
929
|
-
this.window.clearTimeout(this.recentlyActivatedTimerRef);
|
|
930
|
-
// clean up document event listener
|
|
931
|
-
if (this.documentMouseLeaveHandler) {
|
|
932
|
-
document.removeEventListener('mouseleave', this.documentMouseLeaveHandler);
|
|
412
|
+
onPointerLeave(event) {
|
|
413
|
+
if (event.pointerType === 'touch') {
|
|
414
|
+
return;
|
|
933
415
|
}
|
|
416
|
+
this.rootContext.closeOnHover();
|
|
934
417
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
// skip opening if user explicitly dismissed this menu
|
|
943
|
-
if (this.#userDismissedByClick() && itemValue === this.#previousValue()) {
|
|
418
|
+
/**
|
|
419
|
+
* Keyboard navigation inside the open panel: Down/Up move between the panel's focusable items in
|
|
420
|
+
* DOM order, Home/End jump to the first/last, and Up from the first item returns focus to the
|
|
421
|
+
* trigger. (Tab keeps working natively; Escape is handled by the dismissable layer.)
|
|
422
|
+
*/
|
|
423
|
+
onKeydown(event) {
|
|
424
|
+
if (event.key !== ARROW_DOWN && event.key !== ARROW_UP && event.key !== HOME && event.key !== END) {
|
|
944
425
|
return;
|
|
945
426
|
}
|
|
946
|
-
this
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
427
|
+
// If the key originates from a nested navigation menu rendered inside this popup, let that
|
|
428
|
+
// menu's own roving group / popup handle it — otherwise both react and focus jumps/skips.
|
|
429
|
+
const nestedRoot = event.target.closest('[rdxNavigationMenuRoot]');
|
|
430
|
+
if (nestedRoot && this.elementRef.nativeElement.contains(nestedRoot)) {
|
|
431
|
+
return;
|
|
950
432
|
}
|
|
951
|
-
|
|
952
|
-
|
|
433
|
+
const candidates = getTabbableCandidates(this.elementRef.nativeElement);
|
|
434
|
+
if (candidates.length === 0) {
|
|
435
|
+
return;
|
|
953
436
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
}
|
|
959
|
-
onContentEnter() {
|
|
960
|
-
this.window.clearTimeout(this.closeTimerRef);
|
|
961
|
-
}
|
|
962
|
-
onContentLeave() {
|
|
963
|
-
this.startCloseTimer();
|
|
964
|
-
}
|
|
965
|
-
handleClose() {
|
|
966
|
-
this.actionSubject$.next({ action: RdxNavigationMenuAction.CLOSE });
|
|
967
|
-
}
|
|
968
|
-
onItemSelect(itemValue) {
|
|
969
|
-
const wasOpen = this.#value() === itemValue;
|
|
970
|
-
// if this item just opened and the click would close it,
|
|
971
|
-
// ignore the click during the brief protection window
|
|
972
|
-
if (this.recentlyActivatedItem() === itemValue && wasOpen) {
|
|
437
|
+
event.preventDefault();
|
|
438
|
+
const currentIndex = candidates.indexOf(document.activeElement);
|
|
439
|
+
if (event.key === HOME) {
|
|
440
|
+
focusFirst([candidates[0]]);
|
|
973
441
|
return;
|
|
974
442
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
this.#userDismissedByClick.set(true);
|
|
979
|
-
}
|
|
980
|
-
else {
|
|
981
|
-
this.#userDismissedByClick.set(false);
|
|
982
|
-
}
|
|
983
|
-
this.setValue(newValue);
|
|
984
|
-
}
|
|
985
|
-
onItemDismiss() {
|
|
986
|
-
this.setValue('');
|
|
987
|
-
}
|
|
988
|
-
onViewportContentChange(contentValue, contentData) {
|
|
989
|
-
const newMap = new Map(this.#viewportContent());
|
|
990
|
-
newMap.set(contentValue, contentData);
|
|
991
|
-
this.#viewportContent.set(newMap);
|
|
992
|
-
}
|
|
993
|
-
onViewportContentRemove(contentValue) {
|
|
994
|
-
const newMap = new Map(this.#viewportContent());
|
|
995
|
-
if (newMap.has(contentValue)) {
|
|
996
|
-
newMap.delete(contentValue);
|
|
997
|
-
this.#viewportContent.set(newMap);
|
|
443
|
+
if (event.key === END) {
|
|
444
|
+
focusFirst([candidates[candidates.length - 1]]);
|
|
445
|
+
return;
|
|
998
446
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
this.window.clearTimeout(this.recentlyActivatedTimerRef);
|
|
1004
|
-
this.recentlyActivatedItem.set(value);
|
|
1005
|
-
this.recentlyActivatedTimerRef = this.window.setTimeout(() => {
|
|
1006
|
-
this.recentlyActivatedItem.set(null);
|
|
1007
|
-
}, this.clickIgnoreDuration);
|
|
447
|
+
if (event.key === ARROW_DOWN) {
|
|
448
|
+
const next = currentIndex < candidates.length - 1 ? currentIndex + 1 : 0;
|
|
449
|
+
focusFirst([candidates[next]]);
|
|
450
|
+
return;
|
|
1008
451
|
}
|
|
1009
|
-
//
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
}
|
|
1013
|
-
startCloseTimer() {
|
|
1014
|
-
this.window.clearTimeout(this.closeTimerRef);
|
|
1015
|
-
this.closeTimerRef = this.window.setTimeout(() => {
|
|
1016
|
-
// only close if not hovering over any part of the system
|
|
1017
|
-
if (!this.isPointerInSystem()) {
|
|
1018
|
-
this.setValue('');
|
|
1019
|
-
}
|
|
1020
|
-
}, 150);
|
|
1021
|
-
}
|
|
1022
|
-
handleOpen(itemValue) {
|
|
1023
|
-
this.window.clearTimeout(this.closeTimerRef);
|
|
1024
|
-
this.setValue(itemValue);
|
|
1025
|
-
}
|
|
1026
|
-
handleDelayedOpen(itemValue) {
|
|
1027
|
-
const isOpenItem = this.#value() === itemValue;
|
|
1028
|
-
if (isOpenItem) {
|
|
1029
|
-
// if the item is already open, clear close timer
|
|
1030
|
-
this.window.clearTimeout(this.closeTimerRef);
|
|
452
|
+
// ArrowUp: from the first item, return focus to the trigger; otherwise move to the previous.
|
|
453
|
+
if (currentIndex <= 0) {
|
|
454
|
+
this.rootContext.trigger()?.focus();
|
|
1031
455
|
}
|
|
1032
456
|
else {
|
|
1033
|
-
|
|
1034
|
-
this.openTimerRef = this.window.setTimeout(() => {
|
|
1035
|
-
this.window.clearTimeout(this.closeTimerRef);
|
|
1036
|
-
this.setValue(itemValue);
|
|
1037
|
-
}, this.delayDuration);
|
|
457
|
+
focusFirst([candidates[currentIndex - 1]]);
|
|
1038
458
|
}
|
|
1039
459
|
}
|
|
1040
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1041
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "
|
|
460
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuPopup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
461
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNavigationMenuPopup, isStandalone: true, selector: "[rdxNavigationMenuPopup]", outputs: { escapeKeyDown: "escapeKeyDown", pointerDownOutside: "pointerDownOutside", focusOutside: "focusOutside" }, host: { attributes: { "role": "menu", "tabindex": "-1" }, listeners: { "pointerenter": "rootContext.cancelHoverClose()", "pointerleave": "onPointerLeave($event)", "keydown": "onKeydown($event)" }, properties: { "attr.aria-labelledby": "labelledBy()", "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-starting-style": "rootContext.transitionStatus() === \"starting\" ? \"\" : undefined", "attr.data-ending-style": "rootContext.transitionStatus() === \"ending\" ? \"\" : undefined", "attr.data-instant": "rootContext.instant() ? \"\" : undefined", "attr.data-state": "rootContext.isOpen() ? \"open\" : \"closed\"", "attr.data-side": "side()", "attr.data-align": "align()" } }, hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxDismissableLayer }], ngImport: i0 }); }
|
|
1042
462
|
}
|
|
1043
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
463
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuPopup, decorators: [{
|
|
1044
464
|
type: Directive,
|
|
1045
465
|
args: [{
|
|
1046
|
-
selector: '[
|
|
1047
|
-
|
|
466
|
+
selector: '[rdxNavigationMenuPopup]',
|
|
467
|
+
hostDirectives: [RdxPopperContent, RdxDismissableLayer],
|
|
1048
468
|
host: {
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
'aria-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
469
|
+
role: 'menu',
|
|
470
|
+
tabindex: '-1',
|
|
471
|
+
'[attr.aria-labelledby]': 'labelledBy()',
|
|
472
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
473
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
474
|
+
'[attr.data-starting-style]': 'rootContext.transitionStatus() === "starting" ? "" : undefined',
|
|
475
|
+
'[attr.data-ending-style]': 'rootContext.transitionStatus() === "ending" ? "" : undefined',
|
|
476
|
+
'[attr.data-instant]': 'rootContext.instant() ? "" : undefined',
|
|
477
|
+
'[attr.data-state]': 'rootContext.isOpen() ? "open" : "closed"',
|
|
478
|
+
'[attr.data-side]': 'side()',
|
|
479
|
+
'[attr.data-align]': 'align()',
|
|
480
|
+
'(pointerenter)': 'rootContext.cancelHoverClose()',
|
|
481
|
+
'(pointerleave)': 'onPointerLeave($event)',
|
|
482
|
+
'(keydown)': 'onKeydown($event)'
|
|
483
|
+
}
|
|
1055
484
|
}]
|
|
1056
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
1057
|
-
type: Input
|
|
1058
|
-
}], dir: [{
|
|
1059
|
-
type: Input
|
|
1060
|
-
}], clickIgnoreDuration: [{
|
|
1061
|
-
type: Input,
|
|
1062
|
-
args: [{ transform: numberAttribute }]
|
|
1063
|
-
}], delayDuration: [{
|
|
1064
|
-
type: Input,
|
|
1065
|
-
args: [{ transform: numberAttribute }]
|
|
1066
|
-
}], skipDelayDuration: [{
|
|
1067
|
-
type: Input,
|
|
1068
|
-
args: [{ transform: numberAttribute }]
|
|
1069
|
-
}], loop: [{
|
|
1070
|
-
type: Input,
|
|
1071
|
-
args: [{ transform: booleanAttribute }]
|
|
1072
|
-
}], cssAnimation: [{
|
|
1073
|
-
type: Input,
|
|
1074
|
-
args: [{ transform: booleanAttribute }]
|
|
1075
|
-
}], cssOpeningAnimation: [{
|
|
1076
|
-
type: Input,
|
|
1077
|
-
args: [{ transform: booleanAttribute }]
|
|
1078
|
-
}], cssClosingAnimation: [{
|
|
1079
|
-
type: Input,
|
|
1080
|
-
args: [{ transform: booleanAttribute }]
|
|
1081
|
-
}] } });
|
|
485
|
+
}], ctorParameters: () => [], propDecorators: { escapeKeyDown: [{ type: i0.Output, args: ["escapeKeyDown"] }], pointerDownOutside: [{ type: i0.Output, args: ["pointerDownOutside"] }], focusOutside: [{ type: i0.Output, args: ["focusOutside"] }] } });
|
|
1082
486
|
|
|
1083
|
-
|
|
487
|
+
/**
|
|
488
|
+
* Moves the navigation menu popup to a different part of the DOM (by default `document.body`).
|
|
489
|
+
*/
|
|
490
|
+
class RdxNavigationMenuPortal {
|
|
1084
491
|
constructor() {
|
|
1085
|
-
this.
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
this.
|
|
1090
|
-
this.isRootMenu = false;
|
|
1091
|
-
this.parent = inject(RdxNavigationMenuDirective, { optional: true });
|
|
1092
|
-
}
|
|
1093
|
-
set defaultValue(val) {
|
|
1094
|
-
if (val)
|
|
1095
|
-
this.value.set(val);
|
|
1096
|
-
}
|
|
1097
|
-
get dir() {
|
|
1098
|
-
if (!this.parent) {
|
|
1099
|
-
return 'ltr';
|
|
1100
|
-
}
|
|
1101
|
-
return this.parent.dir || 'ltr';
|
|
1102
|
-
}
|
|
1103
|
-
get rootNavigationMenu() {
|
|
1104
|
-
return this.parent?.rootNavigationMenu() || null;
|
|
1105
|
-
}
|
|
1106
|
-
onTriggerEnter(itemValue) {
|
|
1107
|
-
this.setValue(itemValue);
|
|
1108
|
-
}
|
|
1109
|
-
onItemSelect(itemValue) {
|
|
1110
|
-
this.setValue(itemValue);
|
|
1111
|
-
}
|
|
1112
|
-
onItemDismiss() {
|
|
1113
|
-
this.setValue('');
|
|
1114
|
-
}
|
|
1115
|
-
setValue(value) {
|
|
1116
|
-
this.previousValue.set(this.value());
|
|
1117
|
-
this.value.set(value);
|
|
1118
|
-
this.valueChange.emit(value);
|
|
492
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
493
|
+
/**
|
|
494
|
+
* Optional container to portal the popup into. Defaults to `document.body`.
|
|
495
|
+
*/
|
|
496
|
+
this.container = input(...(ngDevMode ? [undefined, { debugName: "container" }] : /* istanbul ignore next */ []));
|
|
1119
497
|
}
|
|
1120
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1121
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
498
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
499
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNavigationMenuPortal, isStandalone: true, selector: "[rdxNavigationMenuPortal]", inputs: { container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-state": "rootContext.isOpen() ? \"open\" : \"closed\"" } }, hostDirectives: [{ directive: i1$2.RdxPortal, inputs: ["container", "container"] }], ngImport: i0 }); }
|
|
1122
500
|
}
|
|
1123
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
501
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuPortal, decorators: [{
|
|
1124
502
|
type: Directive,
|
|
1125
503
|
args: [{
|
|
1126
|
-
selector: '[
|
|
1127
|
-
|
|
504
|
+
selector: '[rdxNavigationMenuPortal]',
|
|
505
|
+
hostDirectives: [
|
|
506
|
+
{
|
|
507
|
+
directive: RdxPortal,
|
|
508
|
+
inputs: ['container']
|
|
509
|
+
}
|
|
510
|
+
],
|
|
1128
511
|
host: {
|
|
1129
|
-
'[attr.data-
|
|
512
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
513
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
514
|
+
'[attr.data-state]': 'rootContext.isOpen() ? "open" : "closed"'
|
|
1130
515
|
}
|
|
1131
516
|
}]
|
|
1132
|
-
}], propDecorators: {
|
|
1133
|
-
type: Input
|
|
1134
|
-
}] } });
|
|
517
|
+
}], propDecorators: { container: [{ type: i0.Input, args: [{ isSignal: true, alias: "container", required: false }] }] } });
|
|
1135
518
|
|
|
1136
|
-
|
|
519
|
+
/**
|
|
520
|
+
* Mounts the popup while the menu is open and waits for CSS exit keyframes before unmounting.
|
|
521
|
+
*
|
|
522
|
+
* ```html
|
|
523
|
+
* <ng-template rdxNavigationMenuPortalPresence>…</ng-template>
|
|
524
|
+
* ```
|
|
525
|
+
*/
|
|
526
|
+
class RdxNavigationMenuPortalPresence {
|
|
527
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuPortalPresence, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
528
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxNavigationMenuPortalPresence, isStandalone: true, selector: "ng-template[rdxNavigationMenuPortalPresence]", providers: [
|
|
529
|
+
provideRdxPresenceContext(() => {
|
|
530
|
+
const context = injectNavigationMenuRootContext();
|
|
531
|
+
return { present: context.isOpen };
|
|
532
|
+
})
|
|
533
|
+
], hostDirectives: [{ directive: i1$3.RdxPresenceDirective }], ngImport: i0 }); }
|
|
534
|
+
}
|
|
535
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuPortalPresence, decorators: [{
|
|
536
|
+
type: Directive,
|
|
537
|
+
args: [{
|
|
538
|
+
selector: 'ng-template[rdxNavigationMenuPortalPresence]',
|
|
539
|
+
hostDirectives: [RdxPresenceDirective],
|
|
540
|
+
providers: [
|
|
541
|
+
provideRdxPresenceContext(() => {
|
|
542
|
+
const context = injectNavigationMenuRootContext();
|
|
543
|
+
return { present: context.isOpen };
|
|
544
|
+
})
|
|
545
|
+
]
|
|
546
|
+
}]
|
|
547
|
+
}] });
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Positions the shared popup against the active trigger.
|
|
551
|
+
*/
|
|
552
|
+
class RdxNavigationMenuPositioner {
|
|
1137
553
|
constructor() {
|
|
1138
|
-
|
|
1139
|
-
this.
|
|
1140
|
-
this.item = inject(RdxNavigationMenuItemDirective);
|
|
1141
|
-
this.list = inject(RdxNavigationMenuListDirective);
|
|
1142
|
-
this.rovingFocusItem = inject(RdxRovingFocusItemDirective, { self: true });
|
|
554
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
555
|
+
this.wrapper = inject(RdxPopperContentWrapper);
|
|
1143
556
|
this.elementRef = inject(ElementRef);
|
|
1144
|
-
this.
|
|
1145
|
-
this.
|
|
1146
|
-
this.
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
this.
|
|
1155
|
-
|
|
557
|
+
this.triggerEl = signal(null, ...(ngDevMode ? [{ debugName: "triggerEl" }] : /* istanbul ignore next */ []));
|
|
558
|
+
this.containerEl = signal(this.elementRef.nativeElement, ...(ngDevMode ? [{ debugName: "containerEl" }] : /* istanbul ignore next */ []));
|
|
559
|
+
this.graceArea = useGraceArea(this.triggerEl, this.containerEl);
|
|
560
|
+
/**
|
|
561
|
+
* An element to position the popup against. Defaults to the active trigger.
|
|
562
|
+
*/
|
|
563
|
+
this.anchor = input(...(ngDevMode ? [undefined, { debugName: "anchor" }] : /* istanbul ignore next */ []));
|
|
564
|
+
/**
|
|
565
|
+
* The preferred side of the trigger to render against when open.
|
|
566
|
+
*/
|
|
567
|
+
this.side = input('bottom', ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
568
|
+
/**
|
|
569
|
+
* Distance between the trigger and the popup in pixels.
|
|
570
|
+
*/
|
|
571
|
+
this.sideOffset = input(0, { ...(ngDevMode ? { debugName: "sideOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
572
|
+
/**
|
|
573
|
+
* How to align the popup relative to the specified side.
|
|
574
|
+
*/
|
|
575
|
+
this.align = input('center', ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
576
|
+
/**
|
|
577
|
+
* An offset in pixels from the `start` or `end` alignment options.
|
|
578
|
+
*/
|
|
579
|
+
this.alignOffset = input(0, { ...(ngDevMode ? { debugName: "alignOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
580
|
+
/**
|
|
581
|
+
* Minimum distance to maintain between the arrow and the edges of the popup.
|
|
582
|
+
*/
|
|
583
|
+
this.arrowPadding = input(5, { ...(ngDevMode ? { debugName: "arrowPadding" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
584
|
+
/**
|
|
585
|
+
* Whether to override side and alignment preferences to prevent collisions.
|
|
586
|
+
*/
|
|
587
|
+
this.avoidCollisions = input(true, { ...(ngDevMode ? { debugName: "avoidCollisions" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
588
|
+
/**
|
|
589
|
+
* The element used as the collision boundary.
|
|
590
|
+
*/
|
|
591
|
+
this.collisionBoundary = input(...(ngDevMode ? [undefined, { debugName: "collisionBoundary" }] : /* istanbul ignore next */ []));
|
|
592
|
+
/**
|
|
593
|
+
* Distance in pixels from the boundary edges where collision detection should occur.
|
|
594
|
+
*/
|
|
595
|
+
this.collisionPadding = input(5, ...(ngDevMode ? [{ debugName: "collisionPadding" }] : /* istanbul ignore next */ []));
|
|
596
|
+
/**
|
|
597
|
+
* The sticky behavior on the alignment axis.
|
|
598
|
+
*/
|
|
599
|
+
this.sticky = input('partial', ...(ngDevMode ? [{ debugName: "sticky" }] : /* istanbul ignore next */ []));
|
|
600
|
+
/**
|
|
601
|
+
* Whether to hide the popup when the trigger becomes fully occluded.
|
|
602
|
+
*/
|
|
603
|
+
this.hideWhenDetached = input(false, { ...(ngDevMode ? { debugName: "hideWhenDetached" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
604
|
+
/**
|
|
605
|
+
* The CSS position strategy used by Floating UI.
|
|
606
|
+
*/
|
|
607
|
+
this.positionStrategy = input('fixed', ...(ngDevMode ? [{ debugName: "positionStrategy" }] : /* istanbul ignore next */ []));
|
|
608
|
+
/**
|
|
609
|
+
* Whether to update position on every animation frame.
|
|
610
|
+
*/
|
|
611
|
+
this.updatePositionStrategy = input('always', ...(ngDevMode ? [{ debugName: "updatePositionStrategy" }] : /* istanbul ignore next */ []));
|
|
612
|
+
effect(() => this.triggerEl.set(this.rootContext.trigger() ?? null));
|
|
613
|
+
// Keep the menu open while the pointer travels from the trigger to the popup; close once it
|
|
614
|
+
// leaves the grace area between them.
|
|
615
|
+
this.graceArea.onPointerExit(() => this.rootContext.closeOnHover());
|
|
616
|
+
}
|
|
617
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuPositioner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
618
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNavigationMenuPositioner, isStandalone: true, selector: "[rdxNavigationMenuPositioner]", inputs: { anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, sideOffset: { classPropertyName: "sideOffset", publicName: "sideOffset", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, alignOffset: { classPropertyName: "alignOffset", publicName: "alignOffset", isSignal: true, isRequired: false, transformFunction: null }, arrowPadding: { classPropertyName: "arrowPadding", publicName: "arrowPadding", isSignal: true, isRequired: false, transformFunction: null }, avoidCollisions: { classPropertyName: "avoidCollisions", publicName: "avoidCollisions", isSignal: true, isRequired: false, transformFunction: null }, collisionBoundary: { classPropertyName: "collisionBoundary", publicName: "collisionBoundary", isSignal: true, isRequired: false, transformFunction: null }, collisionPadding: { classPropertyName: "collisionPadding", publicName: "collisionPadding", isSignal: true, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, hideWhenDetached: { classPropertyName: "hideWhenDetached", publicName: "hideWhenDetached", isSignal: true, isRequired: false, transformFunction: null }, positionStrategy: { classPropertyName: "positionStrategy", publicName: "positionStrategy", isSignal: true, isRequired: false, transformFunction: null }, updatePositionStrategy: { classPropertyName: "updatePositionStrategy", publicName: "updatePositionStrategy", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-anchor-hidden": "wrapper.anchorHidden() ? \"\" : undefined", "attr.data-align": "wrapper.placedAlign()", "attr.data-side": "wrapper.placedSide()", "attr.data-instant": "rootContext.instant() ? \"\" : undefined", "style": "{\n '--anchor-width': 'var(--radix-popper-anchor-width)',\n '--anchor-height': 'var(--radix-popper-anchor-height)',\n '--available-width': 'var(--radix-popper-available-width)',\n '--available-height': 'var(--radix-popper-available-height)',\n '--positioner-width': 'var(--radix-popper-content-wrapper-width)',\n '--positioner-height': 'var(--radix-popper-content-wrapper-height)',\n '--transform-origin': 'var(--radix-popper-transform-origin)'\n }" } }, providers: [
|
|
619
|
+
provideRdxPopperContentConfig({ arrowPadding: 5, collisionPadding: 5, updatePositionStrategy: 'always' })
|
|
620
|
+
], hostDirectives: [{ directive: i1.RdxPopperContentWrapper, inputs: ["anchor", "anchor", "side", "side", "sideOffset", "sideOffset", "align", "align", "alignOffset", "alignOffset", "arrowPadding", "arrowPadding", "avoidCollisions", "avoidCollisions", "collisionBoundary", "collisionBoundary", "collisionPadding", "collisionPadding", "sticky", "sticky", "hideWhenDetached", "hideWhenDetached", "positionStrategy", "positionStrategy", "updatePositionStrategy", "updatePositionStrategy"] }], ngImport: i0 }); }
|
|
621
|
+
}
|
|
622
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuPositioner, decorators: [{
|
|
623
|
+
type: Directive,
|
|
624
|
+
args: [{
|
|
625
|
+
selector: '[rdxNavigationMenuPositioner]',
|
|
626
|
+
providers: [
|
|
627
|
+
provideRdxPopperContentConfig({ arrowPadding: 5, collisionPadding: 5, updatePositionStrategy: 'always' })
|
|
628
|
+
],
|
|
629
|
+
hostDirectives: [
|
|
630
|
+
{
|
|
631
|
+
directive: RdxPopperContentWrapper,
|
|
632
|
+
inputs: [
|
|
633
|
+
'anchor',
|
|
634
|
+
'side',
|
|
635
|
+
'sideOffset',
|
|
636
|
+
'align',
|
|
637
|
+
'alignOffset',
|
|
638
|
+
'arrowPadding',
|
|
639
|
+
'avoidCollisions',
|
|
640
|
+
'collisionBoundary',
|
|
641
|
+
'collisionPadding',
|
|
642
|
+
'sticky',
|
|
643
|
+
'hideWhenDetached',
|
|
644
|
+
'positionStrategy',
|
|
645
|
+
'updatePositionStrategy'
|
|
646
|
+
]
|
|
647
|
+
}
|
|
648
|
+
],
|
|
649
|
+
host: {
|
|
650
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
651
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
652
|
+
'[attr.data-anchor-hidden]': 'wrapper.anchorHidden() ? "" : undefined',
|
|
653
|
+
'[attr.data-align]': 'wrapper.placedAlign()',
|
|
654
|
+
'[attr.data-side]': 'wrapper.placedSide()',
|
|
655
|
+
'[attr.data-instant]': 'rootContext.instant() ? "" : undefined',
|
|
656
|
+
'[style]': `{
|
|
657
|
+
'--anchor-width': 'var(--radix-popper-anchor-width)',
|
|
658
|
+
'--anchor-height': 'var(--radix-popper-anchor-height)',
|
|
659
|
+
'--available-width': 'var(--radix-popper-available-width)',
|
|
660
|
+
'--available-height': 'var(--radix-popper-available-height)',
|
|
661
|
+
'--positioner-width': 'var(--radix-popper-content-wrapper-width)',
|
|
662
|
+
'--positioner-height': 'var(--radix-popper-content-wrapper-height)',
|
|
663
|
+
'--transform-origin': 'var(--radix-popper-transform-origin)'
|
|
664
|
+
}`
|
|
665
|
+
}
|
|
666
|
+
}]
|
|
667
|
+
}], ctorParameters: () => [], propDecorators: { anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: false }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], sideOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideOffset", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], alignOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignOffset", required: false }] }], arrowPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "arrowPadding", required: false }] }], avoidCollisions: [{ type: i0.Input, args: [{ isSignal: true, alias: "avoidCollisions", required: false }] }], collisionBoundary: [{ type: i0.Input, args: [{ isSignal: true, alias: "collisionBoundary", required: false }] }], collisionPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "collisionPadding", required: false }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], hideWhenDetached: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideWhenDetached", required: false }] }], positionStrategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "positionStrategy", required: false }] }], updatePositionStrategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "updatePositionStrategy", required: false }] }] } });
|
|
668
|
+
|
|
669
|
+
const context = () => contextFor(inject(RdxNavigationMenuRoot));
|
|
670
|
+
/**
|
|
671
|
+
* Groups all parts of the navigation menu.
|
|
672
|
+
*
|
|
673
|
+
* Holds the shared open state: `value` identifies the currently open item, and the menu is open
|
|
674
|
+
* whenever `value` is non-null. A single popup (Portal → Positioner → Popup → Viewport) is shared
|
|
675
|
+
* between every item and anchored to the active trigger.
|
|
676
|
+
*/
|
|
677
|
+
class RdxNavigationMenuRoot {
|
|
678
|
+
constructor() {
|
|
679
|
+
this.popper = inject(RdxPopper);
|
|
680
|
+
this.destroyRef = inject(DestroyRef);
|
|
681
|
+
this.parentRoot = inject(RdxNavigationMenuRoot, { optional: true, skipSelf: true });
|
|
682
|
+
/** Whether this root is nested inside another navigation menu's content. */
|
|
683
|
+
this.nested = !!this.parentRoot;
|
|
684
|
+
this.baseId = `rdx-nav-menu-${generateId()}`;
|
|
685
|
+
/**
|
|
686
|
+
* The value of the navigation menu item that should be currently open.
|
|
687
|
+
*/
|
|
688
|
+
this.value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
689
|
+
/**
|
|
690
|
+
* The uncontrolled value of the item that should be initially open.
|
|
691
|
+
*/
|
|
692
|
+
this.defaultValue = input(null, ...(ngDevMode ? [{ debugName: "defaultValue" }] : /* istanbul ignore next */ []));
|
|
693
|
+
/**
|
|
694
|
+
* The orientation of the navigation menu.
|
|
695
|
+
*/
|
|
696
|
+
this.orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
697
|
+
/**
|
|
698
|
+
* The reading direction of the navigation menu.
|
|
699
|
+
*/
|
|
700
|
+
this.dir = input('ltr', ...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
|
|
701
|
+
/**
|
|
702
|
+
* Whether keyboard navigation loops from the last item back to the first and vice versa.
|
|
703
|
+
*/
|
|
704
|
+
this.loop = input(false, { ...(ngDevMode ? { debugName: "loop" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
705
|
+
/**
|
|
706
|
+
* How long to wait before opening the menu on hover, in milliseconds.
|
|
707
|
+
*/
|
|
708
|
+
this.delay = input(50, { ...(ngDevMode ? { debugName: "delay" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
709
|
+
/**
|
|
710
|
+
* How long to wait before closing the menu after the pointer leaves, in milliseconds.
|
|
711
|
+
*/
|
|
712
|
+
this.closeDelay = input(50, { ...(ngDevMode ? { debugName: "closeDelay" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
713
|
+
/**
|
|
714
|
+
* Emits when the open item changes.
|
|
715
|
+
*/
|
|
716
|
+
this.onValueChange = output();
|
|
717
|
+
/**
|
|
718
|
+
* Emits whenever the menu opens or closes.
|
|
719
|
+
*/
|
|
720
|
+
this.onOpenChange = output();
|
|
721
|
+
/**
|
|
722
|
+
* Emits after any enter/exit transition completes.
|
|
723
|
+
*/
|
|
724
|
+
this.onOpenChangeComplete = output();
|
|
725
|
+
this.hasAppliedDefaultValue = false;
|
|
726
|
+
this.transition = useTransitionStatus((open) => {
|
|
727
|
+
this.instant.set(false);
|
|
728
|
+
this.onOpenChangeComplete.emit(open);
|
|
729
|
+
});
|
|
730
|
+
this.instant = signal(false, ...(ngDevMode ? [{ debugName: "instant" }] : /* istanbul ignore next */ []));
|
|
731
|
+
this.transitionStatus = this.transition.status;
|
|
732
|
+
this.previousValue = signal(null, ...(ngDevMode ? [{ debugName: "previousValue" }] : /* istanbul ignore next */ []));
|
|
733
|
+
this.isOpen = computed(() => this.value() !== null, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
|
|
734
|
+
this.trigger = signal(undefined, ...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
|
|
735
|
+
this.triggers = signal([], ...(ngDevMode ? [{ debugName: "triggers" }] : /* istanbul ignore next */ []));
|
|
736
|
+
this.contents = signal(new Map(), ...(ngDevMode ? [{ debugName: "contents" }] : /* istanbul ignore next */ []));
|
|
737
|
+
this.activeContent = computed(() => {
|
|
738
|
+
const value = this.value() ?? this.previousValue();
|
|
739
|
+
return value ? this.contents().get(value) : undefined;
|
|
740
|
+
}, ...(ngDevMode ? [{ debugName: "activeContent" }] : /* istanbul ignore next */ []));
|
|
741
|
+
this.registeredTriggers = new Map();
|
|
742
|
+
this.viewportTriggerChange = new Set();
|
|
743
|
+
let previousOpen = this.isOpen();
|
|
1156
744
|
effect(() => {
|
|
1157
|
-
this.
|
|
745
|
+
const defaultValue = this.defaultValue();
|
|
746
|
+
if (!this.hasAppliedDefaultValue && defaultValue !== null) {
|
|
747
|
+
this.hasAppliedDefaultValue = true;
|
|
748
|
+
this.value.set(defaultValue);
|
|
749
|
+
}
|
|
1158
750
|
});
|
|
1159
751
|
effect(() => {
|
|
1160
|
-
const
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
752
|
+
const open = this.isOpen();
|
|
753
|
+
if (open !== previousOpen) {
|
|
754
|
+
previousOpen = open;
|
|
755
|
+
untracked(() => this.transition.start(open));
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
// Anchor the shared popper to the active trigger.
|
|
759
|
+
effect(() => this.popper.anchorOverride.set(this.trigger()));
|
|
760
|
+
this.destroyRef.onDestroy(() => {
|
|
761
|
+
this.clearHoverTimers();
|
|
762
|
+
if (this.instantFrame !== undefined) {
|
|
763
|
+
cancelAnimationFrame(this.instantFrame);
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
contentId(value) {
|
|
768
|
+
return `${this.baseId}-content-${value}`;
|
|
769
|
+
}
|
|
770
|
+
triggerId(value) {
|
|
771
|
+
return `${this.baseId}-trigger-${value}`;
|
|
772
|
+
}
|
|
773
|
+
setValue(value, reason = 'none', event = new Event('navigation-menu.value-change')) {
|
|
774
|
+
const previous = this.value();
|
|
775
|
+
if (previous === value) {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
const previousTrigger = this.trigger();
|
|
779
|
+
const nextTrigger = value ? this.registeredTriggers.get(value) : undefined;
|
|
780
|
+
const changedTriggerWhileOpen = previous !== null && value !== null && previousTrigger !== nextTrigger;
|
|
781
|
+
this.instant.set(changedTriggerWhileOpen || reason === 'trigger-focus');
|
|
782
|
+
if (changedTriggerWhileOpen) {
|
|
783
|
+
this.scheduleInstantReset();
|
|
784
|
+
if (previousTrigger && nextTrigger) {
|
|
785
|
+
this.viewportTriggerChange.forEach((notify) => notify(previousTrigger, nextTrigger));
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
if (nextTrigger) {
|
|
789
|
+
this.trigger.set(nextTrigger);
|
|
790
|
+
}
|
|
791
|
+
this.previousValue.set(previous);
|
|
792
|
+
this.value.set(value);
|
|
793
|
+
this.onValueChange.emit(value);
|
|
794
|
+
this.onOpenChange.emit({ value, open: value !== null, reason, event });
|
|
795
|
+
}
|
|
796
|
+
open(value, trigger, reason = 'none', event) {
|
|
797
|
+
this.clearHoverTimers();
|
|
798
|
+
// Register the anchor in case this value hasn't been seen yet, but DON'T set `this.trigger`
|
|
799
|
+
// here: setValue must still read the *previous* trigger to detect a trigger switch and drive
|
|
800
|
+
// the viewport morph. It sets `this.trigger` from the registry after that comparison.
|
|
801
|
+
if (!this.registeredTriggers.has(value)) {
|
|
802
|
+
this.registeredTriggers.set(value, trigger);
|
|
803
|
+
}
|
|
804
|
+
this.setValue(value, reason, event);
|
|
805
|
+
}
|
|
806
|
+
close(reason = 'none', event) {
|
|
807
|
+
this.clearHoverTimers();
|
|
808
|
+
if (!this.isOpen()) {
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
this.instant.set(reason !== 'none' && reason !== 'trigger-hover' && reason !== 'list-leave');
|
|
812
|
+
this.setValue(null, reason, event);
|
|
813
|
+
}
|
|
814
|
+
toggle(value, trigger, event) {
|
|
815
|
+
this.clearHoverTimers();
|
|
816
|
+
if (this.value() === value) {
|
|
817
|
+
this.close('trigger-press', event);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
this.open(value, trigger, 'trigger-press', event);
|
|
821
|
+
}
|
|
822
|
+
openOnHover(value, trigger, event) {
|
|
823
|
+
this.clearHoverTimers();
|
|
824
|
+
// Switching between already-open items happens instantly.
|
|
825
|
+
if (this.isOpen()) {
|
|
826
|
+
this.open(value, trigger, 'trigger-hover', event);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
this.openTimer = setTimeout(() => this.open(value, trigger, 'trigger-hover', event), this.delay());
|
|
830
|
+
}
|
|
831
|
+
closeOnHover() {
|
|
832
|
+
this.clearOpenTimer();
|
|
833
|
+
this.clearCloseTimer();
|
|
834
|
+
this.closeTimer = setTimeout(() => this.close('list-leave', new Event('navigation-menu.hover-close')), this.closeDelay());
|
|
835
|
+
}
|
|
836
|
+
cancelHoverOpen() {
|
|
837
|
+
this.clearOpenTimer();
|
|
838
|
+
}
|
|
839
|
+
cancelHoverClose() {
|
|
840
|
+
this.clearCloseTimer();
|
|
841
|
+
}
|
|
842
|
+
registerTrigger(value, trigger) {
|
|
843
|
+
this.registeredTriggers.set(value, trigger);
|
|
844
|
+
this.triggers.update((triggers) => (triggers.includes(trigger) ? triggers : [...triggers, trigger]));
|
|
845
|
+
if (this.value() === value) {
|
|
846
|
+
this.trigger.set(trigger);
|
|
847
|
+
}
|
|
848
|
+
return () => {
|
|
849
|
+
if (this.registeredTriggers.get(value) === trigger) {
|
|
850
|
+
this.registeredTriggers.delete(value);
|
|
851
|
+
}
|
|
852
|
+
this.triggers.update((triggers) => triggers.filter((candidate) => candidate !== trigger));
|
|
853
|
+
if (this.destroyRef.destroyed || this.value() !== value) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
// Defer the close: when an item's `value` changes, the trigger's registration effect
|
|
857
|
+
// unregisters the old value and synchronously re-registers the same element under the new
|
|
858
|
+
// value. Closing immediately would collapse the menu mid-rename, so only close if the
|
|
859
|
+
// element is truly gone (not re-registered under any value) on the next microtask.
|
|
860
|
+
queueMicrotask(() => {
|
|
861
|
+
if (this.destroyRef.destroyed || this.value() !== value) {
|
|
862
|
+
return;
|
|
1165
863
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
this.item.onRootContentClose();
|
|
1170
|
-
}
|
|
1171
|
-
this.hasPointerMoveOpened = false;
|
|
864
|
+
const stillRegistered = [...this.registeredTriggers.values()].includes(trigger);
|
|
865
|
+
if (!stillRegistered) {
|
|
866
|
+
this.close();
|
|
1172
867
|
}
|
|
1173
868
|
});
|
|
1174
|
-
}
|
|
869
|
+
};
|
|
1175
870
|
}
|
|
1176
|
-
|
|
1177
|
-
this.
|
|
1178
|
-
|
|
1179
|
-
|
|
871
|
+
registerContent(entry) {
|
|
872
|
+
this.contents.update((contents) => new Map(contents).set(entry.value, entry));
|
|
873
|
+
return () => {
|
|
874
|
+
this.contents.update((contents) => {
|
|
875
|
+
if (contents.get(entry.value) !== entry) {
|
|
876
|
+
return contents;
|
|
877
|
+
}
|
|
878
|
+
const next = new Map(contents);
|
|
879
|
+
next.delete(entry.value);
|
|
880
|
+
return next;
|
|
881
|
+
});
|
|
882
|
+
};
|
|
1180
883
|
}
|
|
1181
|
-
|
|
1182
|
-
this.
|
|
884
|
+
registerTransitionElement(element) {
|
|
885
|
+
return this.transition.registerElement(element);
|
|
1183
886
|
}
|
|
1184
|
-
|
|
1185
|
-
this.
|
|
887
|
+
registerViewport(onTriggerChange) {
|
|
888
|
+
this.viewportTriggerChange.add(onTriggerChange);
|
|
889
|
+
return () => this.viewportTriggerChange.delete(onTriggerChange);
|
|
1186
890
|
}
|
|
1187
|
-
|
|
1188
|
-
if (this.
|
|
1189
|
-
|
|
891
|
+
scheduleInstantReset() {
|
|
892
|
+
if (this.instantFrame !== undefined) {
|
|
893
|
+
cancelAnimationFrame(this.instantFrame);
|
|
1190
894
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
this.item.onFocusProxyEnter(direction);
|
|
895
|
+
this.instantFrame = requestAnimationFrame(() => {
|
|
896
|
+
this.instantFrame = undefined;
|
|
897
|
+
if (!this.destroyRef.destroyed && this.isOpen()) {
|
|
898
|
+
this.instant.set(false);
|
|
899
|
+
}
|
|
1197
900
|
});
|
|
1198
|
-
// store reference in item directive
|
|
1199
|
-
this.item.focusProxyRef.set(this.focusProxyRef.location.nativeElement);
|
|
1200
|
-
// only add aria-owns component if using viewport
|
|
1201
|
-
if (isRootNavigationMenu(this.context) && this.context.viewport && this.context.viewport()) {
|
|
1202
|
-
this.ariaOwnsRef = this.viewContainerRef.createComponent(RdxNavigationMenuAriaOwnsComponent);
|
|
1203
|
-
this.ariaOwnsRef.instance.contentId = this.contentId;
|
|
1204
|
-
}
|
|
1205
901
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
902
|
+
clearHoverTimers() {
|
|
903
|
+
this.clearOpenTimer();
|
|
904
|
+
this.clearCloseTimer();
|
|
905
|
+
}
|
|
906
|
+
clearOpenTimer() {
|
|
907
|
+
if (this.openTimer !== undefined) {
|
|
908
|
+
clearTimeout(this.openTimer);
|
|
909
|
+
this.openTimer = undefined;
|
|
1211
910
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
911
|
+
}
|
|
912
|
+
clearCloseTimer() {
|
|
913
|
+
if (this.closeTimer !== undefined) {
|
|
914
|
+
clearTimeout(this.closeTimer);
|
|
915
|
+
this.closeTimer = undefined;
|
|
1215
916
|
}
|
|
1216
917
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
918
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
919
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNavigationMenuRoot, isStandalone: true, selector: "[rdxNavigationMenuRoot]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, closeDelay: { classPropertyName: "closeDelay", publicName: "closeDelay", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange", onOpenChange: "onOpenChange", onOpenChangeComplete: "onOpenChangeComplete" }, host: { attributes: { "role": "navigation", "aria-label": "Main" }, properties: { "attr.data-orientation": "orientation()", "attr.dir": "dir()" } }, providers: [provideNavigationMenuRootContext(context)], exportAs: ["rdxNavigationMenuRoot"], hostDirectives: [{ directive: i1.RdxPopper }], ngImport: i0 }); }
|
|
920
|
+
}
|
|
921
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuRoot, decorators: [{
|
|
922
|
+
type: Directive,
|
|
923
|
+
args: [{
|
|
924
|
+
selector: '[rdxNavigationMenuRoot]',
|
|
925
|
+
exportAs: 'rdxNavigationMenuRoot',
|
|
926
|
+
providers: [provideNavigationMenuRootContext(context)],
|
|
927
|
+
hostDirectives: [RdxPopper],
|
|
928
|
+
host: {
|
|
929
|
+
role: 'navigation',
|
|
930
|
+
'aria-label': 'Main',
|
|
931
|
+
'[attr.data-orientation]': 'orientation()',
|
|
932
|
+
'[attr.dir]': 'dir()'
|
|
933
|
+
}
|
|
934
|
+
}]
|
|
935
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], closeDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeDelay", required: false }] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }], onOpenChange: [{ type: i0.Output, args: ["onOpenChange"] }], onOpenChangeComplete: [{ type: i0.Output, args: ["onOpenChangeComplete"] }] } });
|
|
936
|
+
function contextFor(root) {
|
|
937
|
+
return {
|
|
938
|
+
nested: root.nested,
|
|
939
|
+
baseId: root.baseId,
|
|
940
|
+
orientation: root.orientation,
|
|
941
|
+
dir: root.dir,
|
|
942
|
+
loop: root.loop,
|
|
943
|
+
value: root.value,
|
|
944
|
+
previousValue: root.previousValue.asReadonly(),
|
|
945
|
+
isOpen: root.isOpen,
|
|
946
|
+
instant: root.instant.asReadonly(),
|
|
947
|
+
transitionStatus: root.transitionStatus,
|
|
948
|
+
trigger: root.trigger.asReadonly(),
|
|
949
|
+
triggers: root.triggers.asReadonly(),
|
|
950
|
+
activeContent: root.activeContent,
|
|
951
|
+
contentId: (value) => root.contentId(value),
|
|
952
|
+
triggerId: (value) => root.triggerId(value),
|
|
953
|
+
setValue: (value, reason, event) => root.setValue(value, reason, event),
|
|
954
|
+
open: (value, trigger, reason, event) => root.open(value, trigger, reason, event),
|
|
955
|
+
close: (reason, event) => root.close(reason, event),
|
|
956
|
+
toggle: (value, trigger, event) => root.toggle(value, trigger, event),
|
|
957
|
+
openOnHover: (value, trigger, event) => root.openOnHover(value, trigger, event),
|
|
958
|
+
closeOnHover: () => root.closeOnHover(),
|
|
959
|
+
cancelHoverOpen: () => root.cancelHoverOpen(),
|
|
960
|
+
cancelHoverClose: () => root.cancelHoverClose(),
|
|
961
|
+
registerTrigger: (value, trigger) => root.registerTrigger(value, trigger),
|
|
962
|
+
registerContent: (entry) => root.registerContent(entry),
|
|
963
|
+
registerTransitionElement: (element) => root.registerTransitionElement(element),
|
|
964
|
+
registerViewport: (onTriggerChange) => root.registerViewport(onTriggerChange)
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* A button that opens its item's content in the shared popup.
|
|
970
|
+
*/
|
|
971
|
+
class RdxNavigationMenuTrigger {
|
|
972
|
+
constructor() {
|
|
973
|
+
this.item = inject(RdxNavigationMenuItem);
|
|
974
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
975
|
+
this.rovingFocusItem = inject(RdxRovingFocusItemDirective, { self: true });
|
|
976
|
+
this.elementRef = inject(ElementRef);
|
|
977
|
+
this.document = injectDocument();
|
|
978
|
+
/**
|
|
979
|
+
* Whether the trigger should ignore user interaction.
|
|
980
|
+
*/
|
|
981
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
982
|
+
/**
|
|
983
|
+
* Whether the content should also open when the trigger is hovered.
|
|
984
|
+
*/
|
|
985
|
+
this.openOnHover = input(true, { ...(ngDevMode ? { debugName: "openOnHover" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
986
|
+
this.open = computed(() => this.item.isOpen(), ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
987
|
+
// Host element is available in the constructor; the trigger ref does not depend on inputs.
|
|
988
|
+
this.item.triggerRef.set(this.elementRef.nativeElement);
|
|
989
|
+
effect(() => this.rovingFocusItem.setFocusable(!this.disabled()));
|
|
990
|
+
// `value` is an input on the item, so read it in an effect (kept in sync if it ever changes).
|
|
991
|
+
effect(() => this.rovingFocusItem.setTabStopId(this.item.value()));
|
|
992
|
+
effect((onCleanup) => {
|
|
993
|
+
const value = this.item.value();
|
|
994
|
+
const element = this.elementRef.nativeElement;
|
|
995
|
+
// Register untracked: registerTrigger reads root state internally, and we must not make
|
|
996
|
+
// this effect re-run (and tear down the registration) when the open value changes.
|
|
997
|
+
onCleanup(untracked(() => this.rootContext.registerTrigger(value, element)));
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
onClick(event) {
|
|
1001
|
+
if (this.disabled()) {
|
|
1220
1002
|
return;
|
|
1221
|
-
this.wasClickClose = false; // Reset click close flag on enter
|
|
1222
|
-
this.item.wasEscapeCloseRef.set(false); // Reset escape flag
|
|
1223
|
-
this.context.setTriggerPointerState?.(true); // Update context state
|
|
1224
|
-
// if the menu isn't already open for this item, trigger the enter logic
|
|
1225
|
-
if (!this.open()) {
|
|
1226
|
-
if (this.openOnHover() || this.context.value() !== '') {
|
|
1227
|
-
this.context.onTriggerEnter?.(this.item.value());
|
|
1228
|
-
}
|
|
1229
1003
|
}
|
|
1004
|
+
this.rootContext.toggle(this.item.value(), this.elementRef.nativeElement, event);
|
|
1230
1005
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
if (event.pointerType !== 'mouse' ||
|
|
1234
|
-
this.disabled() ||
|
|
1235
|
-
this.wasClickClose ||
|
|
1236
|
-
this.item.wasEscapeCloseRef() ||
|
|
1237
|
-
this.hasPointerMoveOpened ||
|
|
1238
|
-
!isRootNavigationMenu(this.context)) {
|
|
1006
|
+
onPointerEnter(event) {
|
|
1007
|
+
if (this.disabled() || event.pointerType === 'touch' || !this.openOnHover()) {
|
|
1239
1008
|
return;
|
|
1240
1009
|
}
|
|
1241
|
-
this.
|
|
1010
|
+
this.rootContext.cancelHoverClose();
|
|
1011
|
+
this.rootContext.openOnHover(this.item.value(), this.elementRef.nativeElement, event);
|
|
1242
1012
|
}
|
|
1243
1013
|
onPointerLeave(event) {
|
|
1244
|
-
|
|
1245
|
-
if (event.pointerType !== 'mouse' || this.disabled() || !isRootNavigationMenu(this.context)) {
|
|
1014
|
+
if (event.pointerType === 'touch' || !this.openOnHover()) {
|
|
1246
1015
|
return;
|
|
1247
1016
|
}
|
|
1248
|
-
this.
|
|
1249
|
-
this.context.onTriggerLeave?.(); // Trigger leave logic (handles delays)
|
|
1250
|
-
this.hasPointerMoveOpened = false; // Reset flag
|
|
1251
|
-
// reset user dismissal flag if pointer leaves the whole system (trigger + content)
|
|
1252
|
-
if (this.context.resetUserDismissed) {
|
|
1253
|
-
// relay slightly to allow pointer movement to content area without resetting dismissal state
|
|
1254
|
-
setTimeout(() => {
|
|
1255
|
-
if (!this.context.isPointerInSystem?.()) {
|
|
1256
|
-
this.context.resetUserDismissed?.();
|
|
1257
|
-
}
|
|
1258
|
-
}, 50); // small delay for tolerance
|
|
1259
|
-
}
|
|
1017
|
+
this.rootContext.cancelHoverOpen();
|
|
1260
1018
|
}
|
|
1261
|
-
|
|
1262
|
-
|
|
1019
|
+
/**
|
|
1020
|
+
* Open-follows-focus: while the menu is already open, moving keyboard focus (arrow keys via
|
|
1021
|
+
* roving) to another trigger switches the shared popup to that item — matching Base UI, so the
|
|
1022
|
+
* open menu visibly responds to arrow-key navigation. Focus never *opens* a closed menu.
|
|
1023
|
+
*/
|
|
1024
|
+
onFocus() {
|
|
1025
|
+
if (this.disabled() || !this.rootContext.isOpen() || this.open()) {
|
|
1263
1026
|
return;
|
|
1264
|
-
// manually set the `KeyManager` active item to this trigger
|
|
1265
|
-
this.list.setActiveItem(this.item);
|
|
1266
|
-
if (this.context.onItemSelect) {
|
|
1267
|
-
this.context.onItemSelect(this.item.value());
|
|
1268
|
-
// track if this click action resulted in closing the menu
|
|
1269
|
-
this.wasClickClose = !this.open();
|
|
1270
|
-
// reset escape flag if menu was opened by click
|
|
1271
|
-
if (this.open()) {
|
|
1272
|
-
this.item.wasEscapeCloseRef.set(false);
|
|
1273
|
-
}
|
|
1274
1027
|
}
|
|
1028
|
+
this.rootContext.open(this.item.value(), this.elementRef.nativeElement, 'trigger-focus');
|
|
1275
1029
|
}
|
|
1276
1030
|
onKeydown(event) {
|
|
1277
|
-
if (this.disabled())
|
|
1031
|
+
if (this.disabled()) {
|
|
1278
1032
|
return;
|
|
1033
|
+
}
|
|
1034
|
+
const entryKey = this.entryKey();
|
|
1279
1035
|
if (event.key === ENTER || event.key === SPACE) {
|
|
1280
|
-
event.preventDefault();
|
|
1281
|
-
this.
|
|
1282
|
-
// if menu was opened by this keypress, move focus into the content
|
|
1036
|
+
event.preventDefault();
|
|
1037
|
+
this.rootContext.toggle(this.item.value(), this.elementRef.nativeElement, event);
|
|
1283
1038
|
if (this.open()) {
|
|
1284
|
-
|
|
1285
|
-
setTimeout(() => this.item.onEntryKeyDown(), 0);
|
|
1039
|
+
this.focusContent();
|
|
1286
1040
|
}
|
|
1287
1041
|
return;
|
|
1288
1042
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
if (event.key === ARROW_DOWN) {
|
|
1294
|
-
event.preventDefault();
|
|
1043
|
+
if (event.key === entryKey) {
|
|
1044
|
+
event.preventDefault();
|
|
1045
|
+
if (!this.open()) {
|
|
1046
|
+
this.rootContext.open(this.item.value(), this.elementRef.nativeElement, 'trigger-press', event);
|
|
1295
1047
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
// fallback to the standard entry key down approach
|
|
1320
|
-
setTimeout(() => this.item.onEntryKeyDown(), 0);
|
|
1048
|
+
this.focusContent();
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
/** The key that moves focus from the trigger into the open content, based on orientation/dir. */
|
|
1052
|
+
entryKey() {
|
|
1053
|
+
if (this.rootContext.orientation() === 'horizontal') {
|
|
1054
|
+
return ARROW_DOWN;
|
|
1055
|
+
}
|
|
1056
|
+
return this.rootContext.dir() === 'rtl' ? ARROW_LEFT : ARROW_RIGHT;
|
|
1057
|
+
}
|
|
1058
|
+
focusContent() {
|
|
1059
|
+
// Content is rendered into the shared popup, which mounts through Presence + Portal and is
|
|
1060
|
+
// then positioned asynchronously by floating-ui. Until it is positioned the popper keeps it
|
|
1061
|
+
// `visibility: hidden`, so its tabbables aren't focusable yet. Poll across animation frames
|
|
1062
|
+
// until focus actually lands inside the panel (don't give up just because the element exists).
|
|
1063
|
+
const contentId = this.item.contentId();
|
|
1064
|
+
let attempts = 0;
|
|
1065
|
+
const tryFocus = () => {
|
|
1066
|
+
const content = this.document.getElementById(contentId);
|
|
1067
|
+
const candidates = content ? getTabbableCandidates(content) : [];
|
|
1068
|
+
if (candidates.length && focusFirst(candidates)) {
|
|
1321
1069
|
return;
|
|
1322
1070
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
const nextEvent = new KeyboardEvent('keydown', {
|
|
1326
|
-
key: isRTL ? ARROW_LEFT : ARROW_RIGHT,
|
|
1327
|
-
bubbles: true
|
|
1328
|
-
});
|
|
1329
|
-
this.elementRef.nativeElement.dispatchEvent(nextEvent);
|
|
1330
|
-
return;
|
|
1071
|
+
if (attempts++ < 15) {
|
|
1072
|
+
requestAnimationFrame(tryFocus);
|
|
1331
1073
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
event.preventDefault();
|
|
1336
|
-
// emulate a left key press to move to the previous item
|
|
1337
|
-
const nextEvent = new KeyboardEvent('keydown', {
|
|
1338
|
-
key: isRTL ? ARROW_RIGHT : ARROW_LEFT,
|
|
1339
|
-
bubbles: true
|
|
1340
|
-
});
|
|
1341
|
-
this.elementRef.nativeElement.dispatchEvent(nextEvent);
|
|
1342
|
-
return;
|
|
1343
|
-
}
|
|
1344
|
-
// handle vertical navigation and entry into content
|
|
1345
|
-
const verticalEntryKey = isRTL ? ARROW_LEFT : ARROW_RIGHT;
|
|
1346
|
-
const entryKey = isHorizontal ? ARROW_DOWN : verticalEntryKey;
|
|
1347
|
-
if (this.item.contentRef() && event.key === entryKey && event.key !== ARROW_DOWN) {
|
|
1348
|
-
// Skip if it's ArrowDown as we already handled it above
|
|
1349
|
-
event.preventDefault();
|
|
1350
|
-
if (!this.open()) {
|
|
1351
|
-
// if closed, open the menu first
|
|
1352
|
-
this.context.onItemSelect?.(this.item.value());
|
|
1353
|
-
// defer focus movement into content until after state update and render
|
|
1354
|
-
setTimeout(() => this.item.onEntryKeyDown(), 0);
|
|
1355
|
-
}
|
|
1356
|
-
else {
|
|
1357
|
-
// if already open, just move focus into the content
|
|
1358
|
-
this.item.onEntryKeyDown();
|
|
1074
|
+
else if (content) {
|
|
1075
|
+
// Fallback: no tabbable content — focus the panel container itself.
|
|
1076
|
+
content.focus();
|
|
1359
1077
|
}
|
|
1360
|
-
|
|
1361
|
-
|
|
1078
|
+
};
|
|
1079
|
+
requestAnimationFrame(tryFocus);
|
|
1362
1080
|
}
|
|
1363
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1364
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
1081
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1082
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNavigationMenuTrigger, isStandalone: true, selector: "button[rdxNavigationMenuTrigger]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, openOnHover: { classPropertyName: "openOnHover", publicName: "openOnHover", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "button" }, listeners: { "click": "onClick($event)", "pointerenter": "onPointerEnter($event)", "pointerleave": "onPointerLeave($event)", "keydown": "onKeydown($event)", "focus": "onFocus()" }, properties: { "id": "item.triggerId()", "attr.aria-controls": "item.contentId()", "attr.aria-expanded": "open()", "attr.aria-haspopup": "\"menu\"", "attr.data-state": "open() ? \"open\" : \"closed\"", "attr.data-popup-open": "open() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined", "attr.disabled": "disabled() ? \"\" : undefined" } }, hostDirectives: [{ directive: i1$1.RdxRovingFocusItemDirective }], ngImport: i0 }); }
|
|
1365
1083
|
}
|
|
1366
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1084
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuTrigger, decorators: [{
|
|
1367
1085
|
type: Directive,
|
|
1368
1086
|
args: [{
|
|
1369
|
-
selector: '[rdxNavigationMenuTrigger]',
|
|
1087
|
+
selector: 'button[rdxNavigationMenuTrigger]',
|
|
1370
1088
|
hostDirectives: [RdxRovingFocusItemDirective],
|
|
1371
1089
|
host: {
|
|
1372
|
-
|
|
1373
|
-
'[
|
|
1374
|
-
'[attr.
|
|
1375
|
-
'[attr.data-disabled]': 'disabled() ? "" : undefined',
|
|
1376
|
-
'[disabled]': 'disabled() ? true : null',
|
|
1090
|
+
type: 'button',
|
|
1091
|
+
'[id]': 'item.triggerId()',
|
|
1092
|
+
'[attr.aria-controls]': 'item.contentId()',
|
|
1377
1093
|
'[attr.aria-expanded]': 'open()',
|
|
1378
|
-
'[attr.aria-controls]': 'contentId',
|
|
1379
1094
|
'[attr.aria-haspopup]': '"menu"',
|
|
1380
|
-
'
|
|
1381
|
-
'
|
|
1095
|
+
'[attr.data-state]': 'open() ? "open" : "closed"',
|
|
1096
|
+
'[attr.data-popup-open]': 'open() ? "" : undefined',
|
|
1097
|
+
'[attr.data-disabled]': 'disabled() ? "" : undefined',
|
|
1098
|
+
'[attr.disabled]': 'disabled() ? "" : undefined',
|
|
1099
|
+
'(click)': 'onClick($event)',
|
|
1100
|
+
'(pointerenter)': 'onPointerEnter($event)',
|
|
1382
1101
|
'(pointerleave)': 'onPointerLeave($event)',
|
|
1383
|
-
'(click)': 'onClick()',
|
|
1384
1102
|
'(keydown)': 'onKeydown($event)',
|
|
1385
|
-
|
|
1386
|
-
}
|
|
1387
|
-
providers: [{ provide: RdxNavigationMenuFocusableOption, useExisting: RdxNavigationMenuTriggerDirective }]
|
|
1103
|
+
'(focus)': 'onFocus()'
|
|
1104
|
+
}
|
|
1388
1105
|
}]
|
|
1389
|
-
}], ctorParameters: () => [] });
|
|
1106
|
+
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], openOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "openOnHover", required: false }] }] } });
|
|
1390
1107
|
|
|
1391
|
-
|
|
1108
|
+
/**
|
|
1109
|
+
* Clips and animates the active item's content. A single viewport is shared between all items; when
|
|
1110
|
+
* the active item changes the outgoing content is retained as `data-previous` until its CSS
|
|
1111
|
+
* animation/transition completes, and the new content is marked `data-current`.
|
|
1112
|
+
*/
|
|
1113
|
+
class RdxNavigationMenuViewport {
|
|
1392
1114
|
constructor() {
|
|
1393
|
-
this.
|
|
1394
|
-
this.document = injectDocument();
|
|
1395
|
-
this.window = injectWindow();
|
|
1115
|
+
this.rootContext = injectNavigationMenuRootContext();
|
|
1396
1116
|
this.elementRef = inject(ElementRef);
|
|
1397
1117
|
this.viewContainerRef = inject(ViewContainerRef);
|
|
1398
1118
|
this.renderer = inject(Renderer2);
|
|
1399
|
-
this.zone = inject(NgZone);
|
|
1400
|
-
this.destroyRef = inject(DestroyRef);
|
|
1401
1119
|
/**
|
|
1402
|
-
*
|
|
1403
|
-
* Useful for animations.
|
|
1404
|
-
* @default false
|
|
1120
|
+
* Keep the viewport mounted even when the menu is closed.
|
|
1405
1121
|
*/
|
|
1406
|
-
this.forceMount = input(false, ...(ngDevMode ?
|
|
1407
|
-
this.
|
|
1408
|
-
this.
|
|
1409
|
-
this.
|
|
1410
|
-
this.
|
|
1411
|
-
this.
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
if (!isRootNavigationMenu(this.context))
|
|
1418
|
-
return false;
|
|
1419
|
-
return Boolean(this.context.value() || this.forceMount());
|
|
1420
|
-
}, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
1421
|
-
this.dataState = computed(() => getOpenStateLabel(this.isOpen()), ...(ngDevMode ? [{ debugName: "dataState" }] : []));
|
|
1422
|
-
this.viewportSize = computed(() => this._viewportSize(), ...(ngDevMode ? [{ debugName: "viewportSize" }] : []));
|
|
1423
|
-
this._resizeObserver = new ResizeObserver(() => this.updateSize());
|
|
1424
|
-
this.setupViewportEffect();
|
|
1425
|
-
}
|
|
1426
|
-
ngOnInit() {
|
|
1427
|
-
if (isRootNavigationMenu(this.context) && this.context.onViewportChange) {
|
|
1428
|
-
this.context.onViewportChange(this.elementRef.nativeElement);
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
ngOnDestroy() {
|
|
1432
|
-
this._resizeObserver.disconnect();
|
|
1433
|
-
// clean up any remaining nodes/views/subscriptions
|
|
1434
|
-
this._contentNodes().forEach((node) => this.cleanupAfterLeave(node));
|
|
1435
|
-
if (isRootNavigationMenu(this.context) && this.context.onViewportChange) {
|
|
1436
|
-
this.context.onViewportChange(null);
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
onKeydown(event) {
|
|
1440
|
-
if (!this.isOpen())
|
|
1441
|
-
return;
|
|
1442
|
-
const tabbableElements = getTabbableCandidates(this.elementRef.nativeElement);
|
|
1443
|
-
if (!tabbableElements.length)
|
|
1444
|
-
return;
|
|
1445
|
-
const activeElement = this.document.activeElement;
|
|
1446
|
-
const currentIndex = tabbableElements.findIndex((el) => el === activeElement);
|
|
1447
|
-
if (event.key === ARROW_DOWN) {
|
|
1448
|
-
event.preventDefault();
|
|
1449
|
-
const nextIndex = currentIndex >= 0 && currentIndex < tabbableElements.length - 1 ? currentIndex + 1 : 0;
|
|
1450
|
-
tabbableElements[nextIndex]?.focus();
|
|
1451
|
-
}
|
|
1452
|
-
else if (event.key === ARROW_UP) {
|
|
1453
|
-
event.preventDefault();
|
|
1454
|
-
const prevIndex = currentIndex > 0 ? currentIndex - 1 : tabbableElements.length - 1;
|
|
1455
|
-
tabbableElements[prevIndex]?.focus();
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
onPointerEnter() {
|
|
1459
|
-
if (isRootNavigationMenu(this.context) && this.context.onContentEnter) {
|
|
1460
|
-
this.context.onContentEnter();
|
|
1461
|
-
}
|
|
1462
|
-
if (isRootNavigationMenu(this.context) && this.context.setContentPointerState) {
|
|
1463
|
-
this.context.setContentPointerState(true);
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
onPointerLeave() {
|
|
1467
|
-
if (isRootNavigationMenu(this.context) && this.context.onContentLeave) {
|
|
1468
|
-
this.context.onContentLeave();
|
|
1469
|
-
}
|
|
1470
|
-
if (isRootNavigationMenu(this.context) && this.context.setContentPointerState) {
|
|
1471
|
-
this.context.setContentPointerState(false);
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
setupViewportEffect() {
|
|
1122
|
+
this.forceMount = input(false, { ...(ngDevMode ? { debugName: "forceMount" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1123
|
+
this.activationDirection = signal(undefined, ...(ngDevMode ? [{ debugName: "activationDirection" }] : /* istanbul ignore next */ []));
|
|
1124
|
+
this.transitioning = signal(false, ...(ngDevMode ? [{ debugName: "transitioning" }] : /* istanbul ignore next */ []));
|
|
1125
|
+
this.size = signal(null, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1126
|
+
this.current = null;
|
|
1127
|
+
this.previousElement = null;
|
|
1128
|
+
this.resizeObserver = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(() => this.measure()) : null;
|
|
1129
|
+
this.activeContent = computed(() => this.rootContext.activeContent(), ...(ngDevMode ? [{ debugName: "activeContent" }] : /* istanbul ignore next */ []));
|
|
1130
|
+
const unregister = this.rootContext.registerViewport((previous, next) => {
|
|
1131
|
+
this.pendingDirection = getActivationDirection(previous, next);
|
|
1132
|
+
});
|
|
1475
1133
|
effect(() => {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
const currentNodesMap = this._contentNodes();
|
|
1486
|
-
let enteringNode = null;
|
|
1487
|
-
let leavingNode = this._leavingContentNode(); // get potentially already leaving node
|
|
1488
|
-
// 1. Identify Entering Node
|
|
1489
|
-
if (currentActiveValue && allContentData.has(currentActiveValue)) {
|
|
1490
|
-
enteringNode = this.getOrCreateContentNode(currentActiveValue);
|
|
1491
|
-
}
|
|
1492
|
-
// 2. Identify Leaving Node
|
|
1493
|
-
const nodeThatWasActive = previousActiveValue ? currentNodesMap.get(previousActiveValue) : null;
|
|
1494
|
-
// if there was a previously active node, it's different from the entering one,
|
|
1495
|
-
// and it's not already leaving, mark it for removal.
|
|
1496
|
-
if (nodeThatWasActive && nodeThatWasActive !== enteringNode && nodeThatWasActive !== leavingNode) {
|
|
1497
|
-
// if another node was already leaving, force complete its transition
|
|
1498
|
-
if (leavingNode) {
|
|
1499
|
-
this.forceCompleteLeaveTransition(leavingNode);
|
|
1500
|
-
}
|
|
1501
|
-
leavingNode = nodeThatWasActive;
|
|
1502
|
-
this._leavingContentNode.set(leavingNode);
|
|
1503
|
-
}
|
|
1504
|
-
// 3. Handle Entering Node
|
|
1505
|
-
if (enteringNode) {
|
|
1506
|
-
// cancel any pending leave transition for this node if it was leaving
|
|
1507
|
-
if (enteringNode === leavingNode) {
|
|
1508
|
-
this.cancelLeaveTransition(enteringNode);
|
|
1509
|
-
leavingNode = null;
|
|
1510
|
-
this._leavingContentNode.set(null);
|
|
1511
|
-
}
|
|
1512
|
-
// ensure it's in the DOM and set state to open
|
|
1513
|
-
this.addNodeToDOM(enteringNode);
|
|
1514
|
-
this.setNodeState(enteringNode, 'open'); // Triggers enter animation via data-state
|
|
1515
|
-
this._activeContentNode.set(enteringNode);
|
|
1516
|
-
this.updateSize(); // Update size based on the entering node
|
|
1517
|
-
}
|
|
1518
|
-
else {
|
|
1519
|
-
// no node entering, clear active node state
|
|
1520
|
-
this._activeContentNode.set(null);
|
|
1521
|
-
}
|
|
1522
|
-
// 4. Handle Leaving Node
|
|
1523
|
-
if (leavingNode) {
|
|
1524
|
-
if (forceMount) {
|
|
1525
|
-
// if forceMount, just mark as closed, don't trigger removal animation
|
|
1526
|
-
this.setNodeState(leavingNode, 'closed');
|
|
1527
|
-
this._leavingContentNode.set(null); // No longer considered "leaving"
|
|
1528
|
-
}
|
|
1529
|
-
else {
|
|
1530
|
-
// start the leave transition (usePresence handles DOM removal)
|
|
1531
|
-
this.startLeaveTransition(leavingNode);
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
});
|
|
1134
|
+
const entry = this.activeContent();
|
|
1135
|
+
untracked(() => this.render(entry));
|
|
1136
|
+
});
|
|
1137
|
+
inject(DestroyRef).onDestroy(() => {
|
|
1138
|
+
unregister();
|
|
1139
|
+
this.resizeObserver?.disconnect();
|
|
1140
|
+
this.clearCleanupTimer();
|
|
1141
|
+
this.removePrevious();
|
|
1142
|
+
this.current?.view.destroy();
|
|
1535
1143
|
});
|
|
1536
1144
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
const existingNode = this._contentNodes().get(contentValue);
|
|
1540
|
-
if (existingNode && !existingNode.embeddedView.destroyed) {
|
|
1541
|
-
return existingNode;
|
|
1542
|
-
}
|
|
1543
|
-
// create if doesn't exist or view was destroyed
|
|
1544
|
-
if (!isRootNavigationMenu(this.context) || !this.context.viewportContent)
|
|
1545
|
-
return null;
|
|
1546
|
-
const allContentData = this.context.viewportContent();
|
|
1547
|
-
const contentData = allContentData.get(contentValue);
|
|
1548
|
-
const templateRef = contentData?.templateRef;
|
|
1549
|
-
if (!templateRef) {
|
|
1550
|
-
console.error(`No templateRef found for content value: ${contentValue}`);
|
|
1551
|
-
return null;
|
|
1552
|
-
}
|
|
1553
|
-
try {
|
|
1554
|
-
const embeddedView = this.viewContainerRef.createEmbeddedView(templateRef);
|
|
1555
|
-
const container = this.renderer.createElement('div');
|
|
1556
|
-
this.renderer.setAttribute(container, 'class', 'NavigationMenuContentWrapper');
|
|
1557
|
-
this.renderer.setAttribute(container, 'data-content-value', contentValue);
|
|
1558
|
-
embeddedView.rootNodes.forEach((node) => this.renderer.appendChild(container, node));
|
|
1559
|
-
const newNode = {
|
|
1560
|
-
embeddedView,
|
|
1561
|
-
element: container,
|
|
1562
|
-
contentValue,
|
|
1563
|
-
state: 'closed'
|
|
1564
|
-
};
|
|
1565
|
-
const newMap = new Map(this._contentNodes());
|
|
1566
|
-
newMap.set(contentValue, newNode);
|
|
1567
|
-
this._contentNodes.set(newMap);
|
|
1568
|
-
return newNode;
|
|
1569
|
-
}
|
|
1570
|
-
catch (error) {
|
|
1571
|
-
console.error(`Error creating content node for ${contentValue}:`, error);
|
|
1572
|
-
return null;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
// adds node element to viewport DOM if not already present
|
|
1576
|
-
addNodeToDOM(node) {
|
|
1577
|
-
if (!this.elementRef.nativeElement.contains(node.element)) {
|
|
1578
|
-
this.renderer.appendChild(this.elementRef.nativeElement, node.element);
|
|
1579
|
-
// observe size only when added to DOM
|
|
1580
|
-
this._resizeObserver.observe(node.element);
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
// removes node element from viewport DOM
|
|
1584
|
-
removeNodeFromDOM(node) {
|
|
1585
|
-
if (this.elementRef.nativeElement.contains(node.element)) {
|
|
1586
|
-
this._resizeObserver.unobserve(node.element); // stop observing before removal
|
|
1587
|
-
this.renderer.removeChild(this.elementRef.nativeElement, node.element);
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
// updates the data-state and motion attributes
|
|
1591
|
-
setNodeState(node, state) {
|
|
1592
|
-
if (node.state === state)
|
|
1593
|
-
return; // avoid redundant updates
|
|
1594
|
-
node.state = state;
|
|
1595
|
-
this.renderer.setAttribute(node.element, 'data-state', state);
|
|
1596
|
-
// apply motion attribute based on context
|
|
1597
|
-
if (isRootNavigationMenu(this.context) && this.context.viewportContent) {
|
|
1598
|
-
const contentData = this.context.viewportContent().get(node.contentValue);
|
|
1599
|
-
if (contentData?.getMotionAttribute) {
|
|
1600
|
-
// get motion based on current state transition
|
|
1601
|
-
const motionAttr = contentData.getMotionAttribute();
|
|
1602
|
-
if (motionAttr) {
|
|
1603
|
-
this.renderer.setAttribute(node.element, 'data-motion', motionAttr);
|
|
1604
|
-
}
|
|
1605
|
-
else {
|
|
1606
|
-
this.renderer.removeAttribute(node.element, 'data-motion');
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
else {
|
|
1610
|
-
this.renderer.removeAttribute(node.element, 'data-motion');
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
// apply A11y attributes (might only be needed on open?)
|
|
1614
|
-
if (state === 'open') {
|
|
1615
|
-
this.applyA11yAttributes(node);
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
// apply A11y attributes to the first child element
|
|
1619
|
-
applyA11yAttributes(node) {
|
|
1620
|
-
if (!isRootNavigationMenu(this.context) || !this.context.viewportContent)
|
|
1145
|
+
render(entry) {
|
|
1146
|
+
if (!entry) {
|
|
1621
1147
|
return;
|
|
1622
|
-
const contentData = this.context.viewportContent().get(node.contentValue);
|
|
1623
|
-
if (contentData?.additionalAttrs && node.embeddedView.rootNodes.length > 0) {
|
|
1624
|
-
const firstRootNode = node.embeddedView.rootNodes[0];
|
|
1625
|
-
if (firstRootNode) {
|
|
1626
|
-
Object.entries(contentData.additionalAttrs).forEach(([attr, value]) => {
|
|
1627
|
-
this.renderer.setAttribute(firstRootNode, attr, value);
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
1148
|
}
|
|
1631
|
-
|
|
1632
|
-
startLeaveTransition(node) {
|
|
1633
|
-
// ensure node exists and isn't already leaving with an active subscription
|
|
1634
|
-
if (!node || node.transitionSubscription) {
|
|
1635
|
-
node.transitionSubscription?.unsubscribe();
|
|
1149
|
+
if (this.current?.value === entry.value) {
|
|
1636
1150
|
return;
|
|
1637
1151
|
}
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1152
|
+
// Snapshot the outgoing content so it can animate out as `data-previous`.
|
|
1153
|
+
if (this.current) {
|
|
1154
|
+
this.resizeObserver?.unobserve(this.current.measureTarget);
|
|
1155
|
+
this.startLeave(this.current.element);
|
|
1156
|
+
this.current.view.destroy();
|
|
1157
|
+
}
|
|
1158
|
+
const view = this.viewContainerRef.createEmbeddedView(entry.templateRef);
|
|
1159
|
+
view.detectChanges();
|
|
1160
|
+
const element = this.renderer.createElement('div');
|
|
1161
|
+
this.renderer.setAttribute(element, 'data-current', '');
|
|
1162
|
+
this.renderer.setAttribute(element, 'id', entry.contentId);
|
|
1163
|
+
this.renderer.setAttribute(element, 'aria-labelledby', entry.triggerId);
|
|
1164
|
+
// Make the panel a valid focus target for keyboard entry when it has no tabbable content.
|
|
1165
|
+
this.renderer.setAttribute(element, 'tabindex', '-1');
|
|
1166
|
+
view.rootNodes.forEach((node) => this.renderer.appendChild(element, node));
|
|
1167
|
+
this.renderer.appendChild(this.elementRef.nativeElement, element);
|
|
1168
|
+
// Measure the content's own root element, not the wrapper: the wrapper is `display: block`
|
|
1169
|
+
// and stretches to the viewport, so measuring it would feed the viewport its own width back
|
|
1170
|
+
// and the popup would balloon to fill the page. The content root sizes to its content.
|
|
1171
|
+
const measureTarget = element.firstElementChild ?? element;
|
|
1172
|
+
this.current = { value: entry.value, view, element, measureTarget };
|
|
1173
|
+
this.resizeObserver?.observe(measureTarget);
|
|
1174
|
+
this.measure();
|
|
1175
|
+
}
|
|
1176
|
+
startLeave(element) {
|
|
1177
|
+
this.removePrevious();
|
|
1178
|
+
const previous = element.cloneNode(true);
|
|
1179
|
+
previous.removeAttribute('data-current');
|
|
1180
|
+
previous.setAttribute('data-previous', '');
|
|
1181
|
+
previous.setAttribute('aria-hidden', 'true');
|
|
1182
|
+
previous.setAttribute('inert', '');
|
|
1183
|
+
removeIds(previous);
|
|
1184
|
+
this.activationDirection.set(this.pendingDirection);
|
|
1185
|
+
this.pendingDirection = undefined;
|
|
1186
|
+
this.previousElement = previous;
|
|
1187
|
+
this.transitioning.set(true);
|
|
1188
|
+
this.renderer.appendChild(this.elementRef.nativeElement, previous);
|
|
1189
|
+
const onEnd = () => this.removePrevious();
|
|
1190
|
+
previous.addEventListener('animationend', onEnd, { once: true });
|
|
1191
|
+
previous.addEventListener('transitionend', onEnd, { once: true });
|
|
1192
|
+
const duration = getMaxTransitionDuration(previous);
|
|
1193
|
+
this.cleanupTimer = setTimeout(onEnd, duration > 0 ? duration + 50 : 0);
|
|
1194
|
+
}
|
|
1195
|
+
removePrevious() {
|
|
1196
|
+
this.clearCleanupTimer();
|
|
1197
|
+
this.previousElement?.remove();
|
|
1198
|
+
this.previousElement = null;
|
|
1199
|
+
this.transitioning.set(false);
|
|
1200
|
+
}
|
|
1201
|
+
clearCleanupTimer() {
|
|
1202
|
+
if (this.cleanupTimer !== undefined) {
|
|
1203
|
+
clearTimeout(this.cleanupTimer);
|
|
1204
|
+
this.cleanupTimer = undefined;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
measure() {
|
|
1208
|
+
const target = this.current?.measureTarget;
|
|
1209
|
+
if (!target || !target.isConnected) {
|
|
1210
|
+
return;
|
|
1674
1211
|
}
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
node.transitionSubscription?.unsubscribe();
|
|
1680
|
-
node.transitionSubscription = null;
|
|
1212
|
+
const width = Math.ceil(target.offsetWidth);
|
|
1213
|
+
const height = Math.ceil(target.offsetHeight);
|
|
1214
|
+
if (width === 0 && height === 0) {
|
|
1215
|
+
return;
|
|
1681
1216
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
* @param node The node that is leaving
|
|
1686
|
-
*/
|
|
1687
|
-
cancelLeaveTransition(node) {
|
|
1688
|
-
node.transitionSubscription?.unsubscribe();
|
|
1689
|
-
node.transitionSubscription = null;
|
|
1690
|
-
}
|
|
1691
|
-
/**
|
|
1692
|
-
* Force completes a leave transition (e.g., if another leave starts)
|
|
1693
|
-
* @param node The node that is leaving
|
|
1694
|
-
*/
|
|
1695
|
-
forceCompleteLeaveTransition(node) {
|
|
1696
|
-
if (node && node.transitionSubscription) {
|
|
1697
|
-
node.transitionSubscription.unsubscribe();
|
|
1698
|
-
// perform cleanup immediately
|
|
1699
|
-
this.cleanupAfterLeave(node);
|
|
1217
|
+
const size = this.size();
|
|
1218
|
+
if (!size || size.width !== width || size.height !== height) {
|
|
1219
|
+
this.size.set({ width, height });
|
|
1700
1220
|
}
|
|
1701
1221
|
}
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
if (!activeNode || !activeNode.isConnected)
|
|
1705
|
-
return;
|
|
1706
|
-
const firstChild = activeNode.firstChild;
|
|
1707
|
-
if (!firstChild)
|
|
1708
|
-
return;
|
|
1709
|
-
this.window.requestAnimationFrame(() => {
|
|
1710
|
-
// keep rAF here for measurement stability
|
|
1711
|
-
activeNode.getBoundingClientRect(); // force layout
|
|
1712
|
-
const width = Math.ceil(firstChild.offsetWidth);
|
|
1713
|
-
const height = Math.ceil(firstChild.offsetHeight);
|
|
1714
|
-
if (width !== 0 || height !== 0) {
|
|
1715
|
-
const currentSize = this._viewportSize();
|
|
1716
|
-
if (!currentSize || currentSize.width !== width || currentSize.height !== height) {
|
|
1717
|
-
this._viewportSize.set({ width, height });
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
else if (this._viewportSize() !== null) {
|
|
1721
|
-
this._viewportSize.set(null);
|
|
1722
|
-
}
|
|
1723
|
-
});
|
|
1724
|
-
}
|
|
1725
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxNavigationMenuViewportDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1726
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.3", type: RdxNavigationMenuViewportDirective, isStandalone: true, selector: "[rdxNavigationMenuViewport]", inputs: { forceMount: { classPropertyName: "forceMount", publicName: "forceMount", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onKeydown($event)", "pointerenter": "onPointerEnter()", "pointerleave": "onPointerLeave()" }, properties: { "attr.data-state": "dataState()", "attr.data-orientation": "context.orientation", "style.--radix-navigation-menu-viewport-width.px": "viewportSize()?.width", "style.--radix-navigation-menu-viewport-height.px": "viewportSize()?.height" } }, ngImport: i0 }); }
|
|
1222
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuViewport, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1223
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxNavigationMenuViewport, isStandalone: true, selector: "[rdxNavigationMenuViewport]", inputs: { forceMount: { classPropertyName: "forceMount", publicName: "forceMount", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-state": "rootContext.isOpen() ? \"open\" : \"closed\"", "attr.data-orientation": "rootContext.orientation()", "attr.data-activation-direction": "activationDirection()", "attr.data-transitioning": "transitioning() ? \"\" : undefined", "style.--popup-width.px": "size()?.width", "style.--popup-height.px": "size()?.height" } }, ngImport: i0 }); }
|
|
1727
1224
|
}
|
|
1728
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1225
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuViewport, decorators: [{
|
|
1729
1226
|
type: Directive,
|
|
1730
1227
|
args: [{
|
|
1731
1228
|
selector: '[rdxNavigationMenuViewport]',
|
|
1732
1229
|
host: {
|
|
1733
|
-
'[attr.data-
|
|
1734
|
-
'[attr.data-
|
|
1735
|
-
'[
|
|
1736
|
-
'[
|
|
1737
|
-
'
|
|
1738
|
-
'
|
|
1739
|
-
'
|
|
1230
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
1231
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
1232
|
+
'[attr.data-state]': 'rootContext.isOpen() ? "open" : "closed"',
|
|
1233
|
+
'[attr.data-orientation]': 'rootContext.orientation()',
|
|
1234
|
+
'[attr.data-activation-direction]': 'activationDirection()',
|
|
1235
|
+
'[attr.data-transitioning]': 'transitioning() ? "" : undefined',
|
|
1236
|
+
'[style.--popup-width.px]': 'size()?.width',
|
|
1237
|
+
'[style.--popup-height.px]': 'size()?.height'
|
|
1740
1238
|
}
|
|
1741
1239
|
}]
|
|
1742
|
-
}], ctorParameters: () => [] });
|
|
1240
|
+
}], ctorParameters: () => [], propDecorators: { forceMount: [{ type: i0.Input, args: [{ isSignal: true, alias: "forceMount", required: false }] }] } });
|
|
1743
1241
|
|
|
1744
|
-
const
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1242
|
+
const navigationMenuImports = [
|
|
1243
|
+
RdxNavigationMenuRoot,
|
|
1244
|
+
RdxNavigationMenuList,
|
|
1245
|
+
RdxNavigationMenuItem,
|
|
1246
|
+
RdxNavigationMenuTrigger,
|
|
1247
|
+
RdxNavigationMenuIcon,
|
|
1248
|
+
RdxNavigationMenuContent,
|
|
1249
|
+
RdxNavigationMenuLink,
|
|
1250
|
+
RdxNavigationMenuPortal,
|
|
1251
|
+
RdxNavigationMenuPortalPresence,
|
|
1252
|
+
RdxNavigationMenuBackdrop,
|
|
1253
|
+
RdxNavigationMenuPositioner,
|
|
1254
|
+
RdxNavigationMenuPopup,
|
|
1255
|
+
RdxNavigationMenuArrow,
|
|
1256
|
+
RdxNavigationMenuViewport
|
|
1756
1257
|
];
|
|
1757
1258
|
class RdxNavigationMenuModule {
|
|
1758
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1759
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1259
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1260
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuModule, imports: [RdxNavigationMenuRoot,
|
|
1261
|
+
RdxNavigationMenuList,
|
|
1262
|
+
RdxNavigationMenuItem,
|
|
1263
|
+
RdxNavigationMenuTrigger,
|
|
1264
|
+
RdxNavigationMenuIcon,
|
|
1265
|
+
RdxNavigationMenuContent,
|
|
1266
|
+
RdxNavigationMenuLink,
|
|
1267
|
+
RdxNavigationMenuPortal,
|
|
1268
|
+
RdxNavigationMenuPortalPresence,
|
|
1269
|
+
RdxNavigationMenuBackdrop,
|
|
1270
|
+
RdxNavigationMenuPositioner,
|
|
1271
|
+
RdxNavigationMenuPopup,
|
|
1272
|
+
RdxNavigationMenuArrow,
|
|
1273
|
+
RdxNavigationMenuViewport], exports: [RdxNavigationMenuRoot,
|
|
1274
|
+
RdxNavigationMenuList,
|
|
1275
|
+
RdxNavigationMenuItem,
|
|
1276
|
+
RdxNavigationMenuTrigger,
|
|
1277
|
+
RdxNavigationMenuIcon,
|
|
1278
|
+
RdxNavigationMenuContent,
|
|
1279
|
+
RdxNavigationMenuLink,
|
|
1280
|
+
RdxNavigationMenuPortal,
|
|
1281
|
+
RdxNavigationMenuPortalPresence,
|
|
1282
|
+
RdxNavigationMenuBackdrop,
|
|
1283
|
+
RdxNavigationMenuPositioner,
|
|
1284
|
+
RdxNavigationMenuPopup,
|
|
1285
|
+
RdxNavigationMenuArrow,
|
|
1286
|
+
RdxNavigationMenuViewport] }); }
|
|
1287
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuModule }); }
|
|
1781
1288
|
}
|
|
1782
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1289
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxNavigationMenuModule, decorators: [{
|
|
1783
1290
|
type: NgModule,
|
|
1784
1291
|
args: [{
|
|
1785
|
-
imports: [...
|
|
1786
|
-
exports: [...
|
|
1292
|
+
imports: [...navigationMenuImports],
|
|
1293
|
+
exports: [...navigationMenuImports]
|
|
1787
1294
|
}]
|
|
1788
1295
|
}] });
|
|
1789
1296
|
|
|
@@ -1791,5 +1298,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
|
|
|
1791
1298
|
* Generated bundle index. Do not edit.
|
|
1792
1299
|
*/
|
|
1793
1300
|
|
|
1794
|
-
export {
|
|
1301
|
+
export { RdxNavigationMenuArrow, RdxNavigationMenuBackdrop, RdxNavigationMenuContent, RdxNavigationMenuIcon, RdxNavigationMenuItem, RdxNavigationMenuLink, RdxNavigationMenuList, RdxNavigationMenuModule, RdxNavigationMenuPopup, RdxNavigationMenuPortal, RdxNavigationMenuPortalPresence, RdxNavigationMenuPositioner, RdxNavigationMenuRoot, RdxNavigationMenuTrigger, RdxNavigationMenuViewport, injectNavigationMenuRootContext, navigationMenuImports, provideNavigationMenuRootContext };
|
|
1795
1302
|
//# sourceMappingURL=radix-ng-primitives-navigation-menu.mjs.map
|