@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,48 +1,236 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import * as i1 from '@
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { inject, model, input, booleanAttribute, output, signal, computed, effect, untracked, Directive, ElementRef, DestroyRef, numberAttribute, afterNextRender, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1 from '@radix-ng/primitives/popper';
|
|
4
|
+
import { RdxPopper, RdxPopperContentWrapper, RdxPopperArrow, RdxPopperContent, provideRdxPopperContentConfig, RdxPopperAnchor } from '@radix-ng/primitives/popper';
|
|
5
|
+
import { createContext, useTransitionStatus, getMaxTransitionDuration } from '@radix-ng/primitives/core';
|
|
6
|
+
import { outputFromObservable, outputToObservable } from '@angular/core/rxjs-interop';
|
|
7
|
+
import * as i2 from '@radix-ng/primitives/dismissable-layer';
|
|
8
|
+
import { RdxDismissableLayer, RdxDismissableLayersContextToken, provideRdxDismissableLayerConfig, RdxDismissableLayerBranch } from '@radix-ng/primitives/dismissable-layer';
|
|
9
|
+
import * as i3 from '@radix-ng/primitives/focus-scope';
|
|
10
|
+
import { RdxFocusScope, provideRdxFocusScopeConfig } from '@radix-ng/primitives/focus-scope';
|
|
11
|
+
import * as i1$1 from '@radix-ng/primitives/portal';
|
|
12
|
+
import { RdxPortal } from '@radix-ng/primitives/portal';
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
const [injectRdxMenuRootContext, provideRdxMenuRootContext] = createContext('RdxMenuRootContext');
|
|
15
|
+
function buildContext(instance) {
|
|
16
|
+
return {
|
|
17
|
+
isOpen: instance.open,
|
|
18
|
+
disabled: instance.disabled,
|
|
19
|
+
modal: instance.modal,
|
|
20
|
+
loopFocus: instance.loopFocus,
|
|
21
|
+
highlightItemOnHover: instance.highlightItemOnHover,
|
|
22
|
+
orientation: instance.orientation,
|
|
23
|
+
closeParentOnEsc: instance.closeParentOnEsc,
|
|
24
|
+
autoFocus: instance.autoFocus.asReadonly(),
|
|
25
|
+
isSubmenu: instance.isSubmenu.asReadonly(),
|
|
26
|
+
hasTriggerInteractionHandler: instance.hasTriggerInteractionHandler.asReadonly(),
|
|
27
|
+
trigger: instance.trigger.asReadonly(),
|
|
28
|
+
transitionStatus: instance.transitionStatus,
|
|
29
|
+
close: () => instance.close(),
|
|
30
|
+
toggle: () => instance.toggle(),
|
|
31
|
+
show: (autoFocus) => instance.show(autoFocus),
|
|
32
|
+
showWithoutAutoFocus: () => instance.show(false),
|
|
33
|
+
registerTrigger: (el) => instance.registerTrigger(el),
|
|
34
|
+
registerTransitionElement: (el) => instance.registerTransitionElement(el),
|
|
35
|
+
registerPopupArrowNavigationHandler: (handler) => instance.registerPopupArrowNavigationHandler(handler),
|
|
36
|
+
registerTriggerInteractionHandler: (handler) => instance.registerTriggerInteractionHandler(handler),
|
|
37
|
+
markAsSubmenu: () => instance.markAsSubmenu(),
|
|
38
|
+
closeParent: () => instance.closeParent(),
|
|
39
|
+
handlePopupArrowNavigation: (offset) => instance.handlePopupArrowNavigation(offset),
|
|
40
|
+
handleTriggerInteraction: (interaction) => instance.handleTriggerInteraction(interaction)
|
|
41
|
+
};
|
|
10
42
|
}
|
|
11
|
-
|
|
43
|
+
const contextFactory = () => buildContext(inject(RdxMenuRoot));
|
|
44
|
+
/**
|
|
45
|
+
* Groups all parts of a menu.
|
|
46
|
+
*/
|
|
47
|
+
class RdxMenuRoot {
|
|
48
|
+
constructor() {
|
|
49
|
+
this.popper = inject(RdxPopper);
|
|
50
|
+
/** Shared open/close transition state machine (completes on the real animationend). */
|
|
51
|
+
this.transition = useTransitionStatus((open) => this.onOpenChangeComplete.emit(open));
|
|
52
|
+
this.hasAppliedDefaultOpen = false;
|
|
53
|
+
/** Whether the menu is currently open. */
|
|
54
|
+
this.open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
55
|
+
/** Whether the menu is initially open. */
|
|
56
|
+
this.defaultOpen = input(false, { ...(ngDevMode ? { debugName: "defaultOpen" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
57
|
+
/** Whether interactions with the menu are disabled. */
|
|
58
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
59
|
+
/** Whether the menu should block outside interactions. */
|
|
60
|
+
this.modal = input(false, { ...(ngDevMode ? { debugName: "modal" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
61
|
+
/** Whether keyboard navigation wraps at list boundaries. */
|
|
62
|
+
this.loopFocus = input(true, { ...(ngDevMode ? { debugName: "loopFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
63
|
+
/** Whether moving the pointer over items should highlight them. */
|
|
64
|
+
this.highlightItemOnHover = input(true, { ...(ngDevMode ? { debugName: "highlightItemOnHover" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
65
|
+
/** The menu orientation. */
|
|
66
|
+
this.orientation = input('vertical', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
67
|
+
/** Whether pressing Escape inside a submenu closes the whole menu chain. */
|
|
68
|
+
this.closeParentOnEsc = input(false, { ...(ngDevMode ? { debugName: "closeParentOnEsc" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
69
|
+
/** Emits when the open state changes. */
|
|
70
|
+
this.onOpenChange = output();
|
|
71
|
+
/** Emits when the open/close CSS transition or animation finishes. */
|
|
72
|
+
this.onOpenChangeComplete = output();
|
|
73
|
+
this.trigger = signal(undefined, ...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
|
|
74
|
+
this.transitionStatus = this.transition.status;
|
|
75
|
+
/** Whether the popup grabs focus when it opens. Set false for menubar hover-switching. */
|
|
76
|
+
this.autoFocus = signal('first', ...(ngDevMode ? [{ debugName: "autoFocus" }] : /* istanbul ignore next */ []));
|
|
77
|
+
this.isSubmenu = signal(false, ...(ngDevMode ? [{ debugName: "isSubmenu" }] : /* istanbul ignore next */ []));
|
|
78
|
+
this.hasTriggerInteractionHandler = signal(false, ...(ngDevMode ? [{ debugName: "hasTriggerInteractionHandler" }] : /* istanbul ignore next */ []));
|
|
79
|
+
this.state = computed(() => (this.open() ? 'open' : 'closed'), ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
80
|
+
effect(() => {
|
|
81
|
+
const defaultOpen = this.defaultOpen();
|
|
82
|
+
if (!this.hasAppliedDefaultOpen && defaultOpen) {
|
|
83
|
+
this.hasAppliedDefaultOpen = true;
|
|
84
|
+
this.open.set(defaultOpen);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
effect(() => this.popper.anchorOverride.set(this.trigger()));
|
|
88
|
+
let previousOpen = this.open();
|
|
89
|
+
effect(() => {
|
|
90
|
+
const open = this.open();
|
|
91
|
+
if (open !== previousOpen) {
|
|
92
|
+
previousOpen = open;
|
|
93
|
+
untracked(() => this.transition.start(open));
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
show(autoFocus = 'first') {
|
|
98
|
+
if (this.disabled()) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.autoFocus.set(autoFocus === true ? 'first' : autoFocus);
|
|
102
|
+
if (!this.open()) {
|
|
103
|
+
this.open.set(true);
|
|
104
|
+
this.onOpenChange.emit(true);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
close() {
|
|
108
|
+
if (this.open()) {
|
|
109
|
+
this.open.set(false);
|
|
110
|
+
this.onOpenChange.emit(false);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
toggle() {
|
|
114
|
+
if (this.disabled()) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (this.open()) {
|
|
118
|
+
this.close();
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
this.show();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
registerTrigger(el) {
|
|
125
|
+
this.registeredTrigger = el;
|
|
126
|
+
this.trigger.set(el);
|
|
127
|
+
return () => {
|
|
128
|
+
if (this.registeredTrigger === el) {
|
|
129
|
+
this.registeredTrigger = undefined;
|
|
130
|
+
this.trigger.set(undefined);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
registerTransitionElement(element) {
|
|
135
|
+
return this.transition.registerElement(element);
|
|
136
|
+
}
|
|
137
|
+
registerPopupArrowNavigationHandler(handler) {
|
|
138
|
+
this.popupArrowNavigationHandler = handler;
|
|
139
|
+
return () => {
|
|
140
|
+
if (this.popupArrowNavigationHandler === handler) {
|
|
141
|
+
this.popupArrowNavigationHandler = undefined;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
handlePopupArrowNavigation(offset) {
|
|
146
|
+
return this.popupArrowNavigationHandler?.(offset) ?? false;
|
|
147
|
+
}
|
|
148
|
+
registerTriggerInteractionHandler(handler) {
|
|
149
|
+
this.triggerInteractionHandler = handler;
|
|
150
|
+
this.hasTriggerInteractionHandler.set(true);
|
|
151
|
+
return () => {
|
|
152
|
+
if (this.triggerInteractionHandler === handler) {
|
|
153
|
+
this.triggerInteractionHandler = undefined;
|
|
154
|
+
this.hasTriggerInteractionHandler.set(false);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
handleTriggerInteraction(interaction) {
|
|
159
|
+
return this.triggerInteractionHandler?.(interaction) ?? false;
|
|
160
|
+
}
|
|
161
|
+
markAsSubmenu() {
|
|
162
|
+
this.isSubmenu.set(true);
|
|
163
|
+
}
|
|
164
|
+
closeParent() {
|
|
165
|
+
this.trigger()?.dispatchEvent(new CustomEvent('rdx-menu-close-parent', { bubbles: true }));
|
|
166
|
+
}
|
|
167
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
168
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuRoot, isStandalone: true, selector: "[rdxMenuRoot],[rdxMenuSubmenuRoot]", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, defaultOpen: { classPropertyName: "defaultOpen", publicName: "defaultOpen", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, modal: { classPropertyName: "modal", publicName: "modal", isSignal: true, isRequired: false, transformFunction: null }, loopFocus: { classPropertyName: "loopFocus", publicName: "loopFocus", isSignal: true, isRequired: false, transformFunction: null }, highlightItemOnHover: { classPropertyName: "highlightItemOnHover", publicName: "highlightItemOnHover", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, closeParentOnEsc: { classPropertyName: "closeParentOnEsc", publicName: "closeParentOnEsc", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", onOpenChange: "onOpenChange", onOpenChangeComplete: "onOpenChangeComplete" }, providers: [provideRdxMenuRootContext(contextFactory)], exportAs: ["rdxMenuRoot"], hostDirectives: [{ directive: i1.RdxPopper }], ngImport: i0 }); }
|
|
169
|
+
}
|
|
170
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuRoot, decorators: [{
|
|
12
171
|
type: Directive,
|
|
13
172
|
args: [{
|
|
14
|
-
selector: '[
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
'[attr.aria-orientation]': '"vertical"'
|
|
19
|
-
}
|
|
173
|
+
selector: '[rdxMenuRoot],[rdxMenuSubmenuRoot]',
|
|
174
|
+
exportAs: 'rdxMenuRoot',
|
|
175
|
+
providers: [provideRdxMenuRootContext(contextFactory)],
|
|
176
|
+
hostDirectives: [RdxPopper]
|
|
20
177
|
}]
|
|
21
|
-
}] });
|
|
178
|
+
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], defaultOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultOpen", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], modal: [{ type: i0.Input, args: [{ isSignal: true, alias: "modal", required: false }] }], loopFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "loopFocus", required: false }] }], highlightItemOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightItemOnHover", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], closeParentOnEsc: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeParentOnEsc", required: false }] }], onOpenChange: [{ type: i0.Output, args: ["onOpenChange"] }], onOpenChangeComplete: [{ type: i0.Output, args: ["onOpenChangeComplete"] }] } });
|
|
22
179
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
180
|
+
/**
|
|
181
|
+
* An optional visual arrow connecting the popup to its trigger.
|
|
182
|
+
* Place it inside `rdxMenuPopup`. Positioning is handled by the shared Popper Arrow primitive.
|
|
183
|
+
*/
|
|
184
|
+
class RdxMenuArrow {
|
|
185
|
+
constructor() {
|
|
186
|
+
this.rootContext = injectRdxMenuRootContext();
|
|
187
|
+
this.wrapper = inject(RdxPopperContentWrapper, { optional: true });
|
|
188
|
+
this.side = computed(() => this.wrapper?.placedSide(), ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
189
|
+
this.align = computed(() => this.wrapper?.placedAlign(), ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
190
|
+
this.uncentered = computed(() => this.wrapper?.arrowUncentered() ?? false, ...(ngDevMode ? [{ debugName: "uncentered" }] : /* istanbul ignore next */ []));
|
|
191
|
+
}
|
|
192
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuArrow, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
193
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxMenuArrow, isStandalone: true, selector: "[rdxMenuArrow]", 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" } }, exportAs: ["rdxMenuArrow"], hostDirectives: [{ directive: i1.RdxPopperArrow }], ngImport: i0 }); }
|
|
26
194
|
}
|
|
27
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
195
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuArrow, decorators: [{
|
|
28
196
|
type: Directive,
|
|
29
197
|
args: [{
|
|
30
|
-
selector: '[
|
|
31
|
-
|
|
198
|
+
selector: '[rdxMenuArrow]',
|
|
199
|
+
exportAs: 'rdxMenuArrow',
|
|
200
|
+
hostDirectives: [RdxPopperArrow],
|
|
201
|
+
host: {
|
|
202
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
203
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
204
|
+
'[attr.data-side]': 'side()',
|
|
205
|
+
'[attr.data-align]': 'align()',
|
|
206
|
+
'[attr.data-uncentered]': 'uncentered() ? "" : undefined'
|
|
207
|
+
}
|
|
32
208
|
}]
|
|
33
209
|
}] });
|
|
34
210
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
211
|
+
/**
|
|
212
|
+
* An optional overlay rendered behind the menu popup.
|
|
213
|
+
* Style it with `position: fixed; inset: 0` and use `data-open` / `data-closed`
|
|
214
|
+
* for CSS animations.
|
|
215
|
+
*/
|
|
216
|
+
class RdxMenuBackdrop {
|
|
217
|
+
constructor() {
|
|
218
|
+
this.rootContext = injectRdxMenuRootContext();
|
|
219
|
+
}
|
|
220
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuBackdrop, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
221
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxMenuBackdrop, isStandalone: true, selector: "[rdxMenuBackdrop]", host: { properties: { "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-state": "rootContext.isOpen() ? \"open\" : \"closed\"", "attr.data-starting-style": "rootContext.transitionStatus() === \"starting\" ? \"\" : undefined", "attr.data-ending-style": "rootContext.transitionStatus() === \"ending\" ? \"\" : undefined" } }, exportAs: ["rdxMenuBackdrop"], ngImport: i0 }); }
|
|
38
222
|
}
|
|
39
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
223
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuBackdrop, decorators: [{
|
|
40
224
|
type: Directive,
|
|
41
225
|
args: [{
|
|
42
|
-
selector: '[
|
|
43
|
-
|
|
226
|
+
selector: '[rdxMenuBackdrop]',
|
|
227
|
+
exportAs: 'rdxMenuBackdrop',
|
|
44
228
|
host: {
|
|
45
|
-
|
|
229
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
230
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
231
|
+
'[attr.data-state]': 'rootContext.isOpen() ? "open" : "closed"',
|
|
232
|
+
'[attr.data-starting-style]': 'rootContext.transitionStatus() === "starting" ? "" : undefined',
|
|
233
|
+
'[attr.data-ending-style]': 'rootContext.transitionStatus() === "ending" ? "" : undefined'
|
|
46
234
|
}
|
|
47
235
|
}]
|
|
48
236
|
}] });
|
|
@@ -54,26 +242,35 @@ function getCheckedState(checked) {
|
|
|
54
242
|
return isIndeterminate(checked) ? 'indeterminate' : checked ? 'checked' : 'unchecked';
|
|
55
243
|
}
|
|
56
244
|
|
|
57
|
-
|
|
245
|
+
const [injectRdxMenuCheckboxItemContext, provideRdxMenuCheckboxItemContext] = createContext('RdxMenuCheckboxItemContext');
|
|
246
|
+
const checkboxItemContextFactory = () => {
|
|
247
|
+
const instance = inject(RdxMenuCheckboxItem);
|
|
248
|
+
return {
|
|
249
|
+
checked: instance.checked
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* A menu item that can be checked or unchecked.
|
|
254
|
+
*/
|
|
255
|
+
class RdxMenuCheckboxItem {
|
|
58
256
|
constructor() {
|
|
59
|
-
this.
|
|
60
|
-
this.
|
|
61
|
-
this.
|
|
62
|
-
this
|
|
63
|
-
this.
|
|
64
|
-
|
|
65
|
-
this.
|
|
257
|
+
this.rootContext = injectRdxMenuRootContext(true);
|
|
258
|
+
this.elementRef = inject(ElementRef);
|
|
259
|
+
this.isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
|
|
260
|
+
/** Whether this item is disabled. */
|
|
261
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
262
|
+
/** Whether toggling closes the menu. Defaults to false — checkbox items stay open. */
|
|
263
|
+
this.closeOnClick = input(false, { ...(ngDevMode ? { debugName: "closeOnClick" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
264
|
+
/** Explicit typeahead label. When set, overrides textContent for character search. */
|
|
265
|
+
this.label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
266
|
+
/** The checked state of the item. */
|
|
267
|
+
this.checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
268
|
+
/** Emits when the checked state changes. */
|
|
269
|
+
this.onCheckedChange = output();
|
|
270
|
+
this.highlighted = computed(() => this.isFocused(), ...(ngDevMode ? [{ debugName: "highlighted" }] : /* istanbul ignore next */ []));
|
|
271
|
+
// Expose helpers for host bindings
|
|
66
272
|
this.isIndeterminate = isIndeterminate;
|
|
67
273
|
this.getCheckedState = getCheckedState;
|
|
68
|
-
effect(() => {
|
|
69
|
-
if (isIndeterminate(this.checked())) {
|
|
70
|
-
this.cdkMenuItemCheckbox.checked = true;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
this.cdkMenuItemCheckbox.checked = !this.checked();
|
|
74
|
-
}
|
|
75
|
-
this.cdkMenuItemCheckbox.disabled = this.disabled();
|
|
76
|
-
});
|
|
77
274
|
}
|
|
78
275
|
onFocus() {
|
|
79
276
|
if (!this.disabled()) {
|
|
@@ -84,54 +281,152 @@ class RdxMenuItemCheckboxDirective {
|
|
|
84
281
|
this.isFocused.set(false);
|
|
85
282
|
}
|
|
86
283
|
onPointerMove(event) {
|
|
87
|
-
if (event.defaultPrevented)
|
|
284
|
+
if (event.defaultPrevented || event.pointerType !== 'mouse' || this.disabled()) {
|
|
88
285
|
return;
|
|
89
|
-
|
|
286
|
+
}
|
|
287
|
+
if (this.rootContext && !this.rootContext.highlightItemOnHover()) {
|
|
90
288
|
return;
|
|
91
|
-
if (!this.disabled()) {
|
|
92
|
-
const item = event.currentTarget;
|
|
93
|
-
item?.focus({ preventScroll: true });
|
|
94
289
|
}
|
|
290
|
+
if (document.activeElement !== this.elementRef.nativeElement) {
|
|
291
|
+
this.elementRef.nativeElement.focus({ preventScroll: true });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
onPointerLeave(event) {
|
|
295
|
+
if (event.pointerType !== 'mouse') {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (document.activeElement === this.elementRef.nativeElement) {
|
|
299
|
+
this.elementRef.nativeElement.closest('[rdxMenuPopup]')?.focus({ preventScroll: true });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
onItemClick() {
|
|
303
|
+
if (this.disabled())
|
|
304
|
+
return;
|
|
305
|
+
this.toggleChecked();
|
|
306
|
+
if (this.closeOnClick())
|
|
307
|
+
this.rootContext?.close();
|
|
95
308
|
}
|
|
96
|
-
|
|
97
|
-
|
|
309
|
+
onActivate(event) {
|
|
310
|
+
if (this.disabled())
|
|
311
|
+
return;
|
|
312
|
+
event.preventDefault();
|
|
313
|
+
this.toggleChecked();
|
|
314
|
+
if (this.closeOnClick())
|
|
315
|
+
this.rootContext?.close();
|
|
316
|
+
}
|
|
317
|
+
toggleChecked() {
|
|
318
|
+
const next = isIndeterminate(this.checked()) ? true : !this.checked();
|
|
319
|
+
this.checked.set(next);
|
|
320
|
+
this.onCheckedChange.emit(next);
|
|
321
|
+
}
|
|
322
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuCheckboxItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
323
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuCheckboxItem, isStandalone: true, selector: "[rdxMenuCheckboxItem]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, closeOnClick: { classPropertyName: "closeOnClick", publicName: "closeOnClick", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", onCheckedChange: "onCheckedChange" }, host: { attributes: { "role": "menuitemcheckbox", "tabindex": "-1" }, listeners: { "focus": "onFocus()", "blur": "onBlur()", "pointermove": "onPointerMove($event)", "pointerleave": "onPointerLeave($event)", "click": "onItemClick()", "keydown.enter": "onActivate($event)", "keydown.space": "onActivate($event)" }, properties: { "attr.aria-checked": "isIndeterminate(checked()) ? \"mixed\" : checked()", "attr.data-state": "getCheckedState(checked())", "attr.data-disabled": "disabled() ? \"\" : undefined", "attr.aria-disabled": "disabled() ? true : undefined", "attr.data-highlighted": "highlighted() ? \"\" : undefined", "attr.data-label": "label() ?? undefined" } }, providers: [provideRdxMenuCheckboxItemContext(checkboxItemContextFactory)], exportAs: ["rdxMenuCheckboxItem"], ngImport: i0 }); }
|
|
98
324
|
}
|
|
99
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
325
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuCheckboxItem, decorators: [{
|
|
100
326
|
type: Directive,
|
|
101
327
|
args: [{
|
|
102
|
-
selector: '[
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
directive: CdkMenuItemCheckbox,
|
|
106
|
-
outputs: ['cdkMenuItemTriggered: menuItemTriggered']
|
|
107
|
-
}
|
|
108
|
-
],
|
|
328
|
+
selector: '[rdxMenuCheckboxItem]',
|
|
329
|
+
exportAs: 'rdxMenuCheckboxItem',
|
|
330
|
+
providers: [provideRdxMenuCheckboxItemContext(checkboxItemContextFactory)],
|
|
109
331
|
host: {
|
|
110
332
|
role: 'menuitemcheckbox',
|
|
333
|
+
tabindex: '-1',
|
|
111
334
|
'[attr.aria-checked]': 'isIndeterminate(checked()) ? "mixed" : checked()',
|
|
112
335
|
'[attr.data-state]': 'getCheckedState(checked())',
|
|
113
|
-
'[attr.data-
|
|
336
|
+
'[attr.data-disabled]': 'disabled() ? "" : undefined',
|
|
337
|
+
'[attr.aria-disabled]': 'disabled() ? true : undefined',
|
|
338
|
+
'[attr.data-highlighted]': 'highlighted() ? "" : undefined',
|
|
339
|
+
'[attr.data-label]': 'label() ?? undefined',
|
|
114
340
|
'(focus)': 'onFocus()',
|
|
115
341
|
'(blur)': 'onBlur()',
|
|
116
|
-
'(pointermove)': 'onPointerMove($event)'
|
|
342
|
+
'(pointermove)': 'onPointerMove($event)',
|
|
343
|
+
'(pointerleave)': 'onPointerLeave($event)',
|
|
344
|
+
'(click)': 'onItemClick()',
|
|
345
|
+
'(keydown.enter)': 'onActivate($event)',
|
|
346
|
+
'(keydown.space)': 'onActivate($event)'
|
|
117
347
|
}
|
|
118
348
|
}]
|
|
119
|
-
}],
|
|
349
|
+
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], closeOnClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnClick", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], onCheckedChange: [{ type: i0.Output, args: ["onCheckedChange"] }] } });
|
|
120
350
|
|
|
121
|
-
|
|
351
|
+
/**
|
|
352
|
+
* Renders when the parent checkbox item is checked or indeterminate.
|
|
353
|
+
* Set `keepMounted` to keep the element in the DOM when unchecked (enables CSS animations).
|
|
354
|
+
*/
|
|
355
|
+
class RdxMenuCheckboxItemIndicator {
|
|
122
356
|
constructor() {
|
|
123
|
-
this.
|
|
124
|
-
|
|
125
|
-
this.
|
|
126
|
-
this.
|
|
127
|
-
this.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
357
|
+
this.itemContext = injectRdxMenuCheckboxItemContext();
|
|
358
|
+
/** Keep the indicator in the DOM when unchecked so CSS exit animations can play. */
|
|
359
|
+
this.keepMounted = input(false, { ...(ngDevMode ? { debugName: "keepMounted" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
360
|
+
this.dataState = computed(() => getCheckedState(this.itemContext.checked()), ...(ngDevMode ? [{ debugName: "dataState" }] : /* istanbul ignore next */ []));
|
|
361
|
+
this.isVisible = computed(() => isIndeterminate(this.itemContext.checked()) || this.itemContext.checked() === true, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
|
|
362
|
+
}
|
|
363
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuCheckboxItemIndicator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
364
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuCheckboxItemIndicator, isStandalone: true, selector: "[rdxMenuCheckboxItemIndicator]", inputs: { keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-state": "dataState()", "attr.data-starting-style": "isVisible() ? \"\" : undefined", "attr.data-ending-style": "!isVisible() ? \"\" : undefined", "style.display": "!keepMounted() && !isVisible() ? \"none\" : null" } }, exportAs: ["rdxMenuCheckboxItemIndicator"], ngImport: i0 }); }
|
|
365
|
+
}
|
|
366
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuCheckboxItemIndicator, decorators: [{
|
|
367
|
+
type: Directive,
|
|
368
|
+
args: [{
|
|
369
|
+
selector: '[rdxMenuCheckboxItemIndicator]',
|
|
370
|
+
exportAs: 'rdxMenuCheckboxItemIndicator',
|
|
371
|
+
host: {
|
|
372
|
+
'[attr.data-state]': 'dataState()',
|
|
373
|
+
'[attr.data-starting-style]': 'isVisible() ? "" : undefined',
|
|
374
|
+
'[attr.data-ending-style]': '!isVisible() ? "" : undefined',
|
|
375
|
+
'[style.display]': '!keepMounted() && !isVisible() ? "none" : null'
|
|
376
|
+
}
|
|
377
|
+
}]
|
|
378
|
+
}], propDecorators: { keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }] } });
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Groups related menu items together.
|
|
382
|
+
*/
|
|
383
|
+
class RdxMenuGroup {
|
|
384
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
385
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxMenuGroup, isStandalone: true, selector: "[rdxMenuGroup]", host: { attributes: { "role": "group" } }, exportAs: ["rdxMenuGroup"], ngImport: i0 }); }
|
|
386
|
+
}
|
|
387
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuGroup, decorators: [{
|
|
388
|
+
type: Directive,
|
|
389
|
+
args: [{
|
|
390
|
+
selector: '[rdxMenuGroup]',
|
|
391
|
+
exportAs: 'rdxMenuGroup',
|
|
392
|
+
host: {
|
|
393
|
+
role: 'group'
|
|
394
|
+
}
|
|
395
|
+
}]
|
|
396
|
+
}] });
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* A label for a menu group.
|
|
400
|
+
*/
|
|
401
|
+
class RdxMenuGroupLabel {
|
|
402
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuGroupLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
403
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxMenuGroupLabel, isStandalone: true, selector: "[rdxMenuGroupLabel]", exportAs: ["rdxMenuGroupLabel"], ngImport: i0 }); }
|
|
404
|
+
}
|
|
405
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuGroupLabel, decorators: [{
|
|
406
|
+
type: Directive,
|
|
407
|
+
args: [{
|
|
408
|
+
selector: '[rdxMenuGroupLabel]',
|
|
409
|
+
exportAs: 'rdxMenuGroupLabel'
|
|
410
|
+
}]
|
|
411
|
+
}] });
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* An individual menu item.
|
|
415
|
+
*/
|
|
416
|
+
class RdxMenuItem {
|
|
417
|
+
constructor() {
|
|
418
|
+
this.rootContext = injectRdxMenuRootContext(true);
|
|
419
|
+
this.elementRef = inject(ElementRef);
|
|
420
|
+
this.isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
|
|
421
|
+
/** Whether this item is disabled. */
|
|
422
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
423
|
+
/** Whether selecting this item closes the menu. */
|
|
424
|
+
this.closeOnClick = input(true, { ...(ngDevMode ? { debugName: "closeOnClick" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
425
|
+
/** Explicit typeahead label. When set, overrides textContent for character search. */
|
|
426
|
+
this.label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
427
|
+
/** Emits when the item is selected. */
|
|
428
|
+
this.onSelect = output();
|
|
429
|
+
this.highlighted = computed(() => this.isFocused(), ...(ngDevMode ? [{ debugName: "highlighted" }] : /* istanbul ignore next */ []));
|
|
135
430
|
}
|
|
136
431
|
onFocus() {
|
|
137
432
|
if (!this.disabled()) {
|
|
@@ -142,83 +437,619 @@ class RdxMenuItemRadioDirective {
|
|
|
142
437
|
this.isFocused.set(false);
|
|
143
438
|
}
|
|
144
439
|
onPointerMove(event) {
|
|
145
|
-
if (event.defaultPrevented)
|
|
440
|
+
if (event.defaultPrevented || event.pointerType !== 'mouse' || this.disabled()) {
|
|
146
441
|
return;
|
|
147
|
-
|
|
442
|
+
}
|
|
443
|
+
if (this.rootContext && !this.rootContext.highlightItemOnHover()) {
|
|
148
444
|
return;
|
|
149
|
-
if (!this.disabled()) {
|
|
150
|
-
const item = event.currentTarget;
|
|
151
|
-
item?.focus({ preventScroll: true });
|
|
152
445
|
}
|
|
446
|
+
if (document.activeElement !== this.elementRef.nativeElement) {
|
|
447
|
+
this.elementRef.nativeElement.focus({ preventScroll: true });
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
onPointerLeave(event) {
|
|
451
|
+
if (event.pointerType !== 'mouse') {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
// Clear highlight when the pointer leaves: move focus back to the popup. A subsequent
|
|
455
|
+
// pointermove on a sibling item re-focuses it, so moving between items still works.
|
|
456
|
+
if (document.activeElement === this.elementRef.nativeElement) {
|
|
457
|
+
this.elementRef.nativeElement.closest('[rdxMenuPopup]')?.focus({ preventScroll: true });
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
onItemClick() {
|
|
461
|
+
if (this.disabled())
|
|
462
|
+
return;
|
|
463
|
+
this.onSelect.emit();
|
|
464
|
+
if (this.closeOnClick())
|
|
465
|
+
this.rootContext?.close();
|
|
153
466
|
}
|
|
154
|
-
|
|
155
|
-
|
|
467
|
+
onActivate(event) {
|
|
468
|
+
if (this.disabled())
|
|
469
|
+
return;
|
|
470
|
+
event.preventDefault();
|
|
471
|
+
this.onSelect.emit();
|
|
472
|
+
if (this.closeOnClick())
|
|
473
|
+
this.rootContext?.close();
|
|
474
|
+
}
|
|
475
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
476
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuItem, isStandalone: true, selector: "[rdxMenuItem]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, closeOnClick: { classPropertyName: "closeOnClick", publicName: "closeOnClick", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect" }, host: { attributes: { "role": "menuitem", "tabindex": "-1" }, listeners: { "focus": "onFocus()", "blur": "onBlur()", "pointermove": "onPointerMove($event)", "pointerleave": "onPointerLeave($event)", "click": "onItemClick()", "keydown.enter": "onActivate($event)", "keydown.space": "onActivate($event)" }, properties: { "attr.data-disabled": "disabled() ? \"\" : undefined", "attr.aria-disabled": "disabled() ? true : undefined", "attr.data-highlighted": "highlighted() ? \"\" : undefined", "attr.data-label": "label() ?? undefined" } }, exportAs: ["rdxMenuItem"], ngImport: i0 }); }
|
|
156
477
|
}
|
|
157
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
478
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuItem, decorators: [{
|
|
158
479
|
type: Directive,
|
|
159
480
|
args: [{
|
|
160
|
-
selector: '[
|
|
161
|
-
|
|
162
|
-
{
|
|
163
|
-
directive: CdkMenuItemRadio,
|
|
164
|
-
outputs: ['cdkMenuItemTriggered: menuItemTriggered']
|
|
165
|
-
}
|
|
166
|
-
],
|
|
481
|
+
selector: '[rdxMenuItem]',
|
|
482
|
+
exportAs: 'rdxMenuItem',
|
|
167
483
|
host: {
|
|
168
|
-
role: '
|
|
169
|
-
|
|
170
|
-
'[attr.data-
|
|
171
|
-
'[attr.
|
|
484
|
+
role: 'menuitem',
|
|
485
|
+
tabindex: '-1',
|
|
486
|
+
'[attr.data-disabled]': 'disabled() ? "" : undefined',
|
|
487
|
+
'[attr.aria-disabled]': 'disabled() ? true : undefined',
|
|
488
|
+
'[attr.data-highlighted]': 'highlighted() ? "" : undefined',
|
|
489
|
+
'[attr.data-label]': 'label() ?? undefined',
|
|
172
490
|
'(focus)': 'onFocus()',
|
|
173
491
|
'(blur)': 'onBlur()',
|
|
174
|
-
'(pointermove)': 'onPointerMove($event)'
|
|
492
|
+
'(pointermove)': 'onPointerMove($event)',
|
|
493
|
+
'(pointerleave)': 'onPointerLeave($event)',
|
|
494
|
+
'(click)': 'onItemClick()',
|
|
495
|
+
'(keydown.enter)': 'onActivate($event)',
|
|
496
|
+
'(keydown.space)': 'onActivate($event)'
|
|
175
497
|
}
|
|
176
498
|
}]
|
|
177
|
-
}],
|
|
499
|
+
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], closeOnClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnClick", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }] } });
|
|
178
500
|
|
|
179
|
-
|
|
501
|
+
/**
|
|
502
|
+
* A menu item that renders as a link.
|
|
503
|
+
*/
|
|
504
|
+
class RdxMenuLinkItem {
|
|
180
505
|
constructor() {
|
|
181
|
-
this.
|
|
182
|
-
this.
|
|
183
|
-
this.
|
|
506
|
+
this.rootContext = injectRdxMenuRootContext(true);
|
|
507
|
+
this.elementRef = inject(ElementRef);
|
|
508
|
+
this.isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
|
|
509
|
+
/** Whether this item is disabled. */
|
|
510
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
511
|
+
/** Whether selecting this item closes the menu. Defaults to false — links navigate by default. */
|
|
512
|
+
this.closeOnClick = input(false, { ...(ngDevMode ? { debugName: "closeOnClick" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
513
|
+
/** Explicit typeahead label. When set, overrides textContent for character search. */
|
|
514
|
+
this.label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
515
|
+
/** Emits when the item is selected. */
|
|
516
|
+
this.onSelect = output();
|
|
517
|
+
this.highlighted = computed(() => this.isFocused(), ...(ngDevMode ? [{ debugName: "highlighted" }] : /* istanbul ignore next */ []));
|
|
518
|
+
}
|
|
519
|
+
onFocus() {
|
|
520
|
+
if (!this.disabled()) {
|
|
521
|
+
this.isFocused.set(true);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
onBlur() {
|
|
525
|
+
this.isFocused.set(false);
|
|
526
|
+
}
|
|
527
|
+
onPointerMove(event) {
|
|
528
|
+
if (event.defaultPrevented || event.pointerType !== 'mouse' || this.disabled()) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
if (this.rootContext && !this.rootContext.highlightItemOnHover()) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (document.activeElement !== this.elementRef.nativeElement) {
|
|
535
|
+
this.elementRef.nativeElement.focus({ preventScroll: true });
|
|
536
|
+
}
|
|
184
537
|
}
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
return
|
|
538
|
+
onPointerLeave(event) {
|
|
539
|
+
if (event.pointerType !== 'mouse') {
|
|
540
|
+
return;
|
|
188
541
|
}
|
|
189
|
-
if (this.
|
|
190
|
-
|
|
542
|
+
if (document.activeElement === this.elementRef.nativeElement) {
|
|
543
|
+
this.elementRef.nativeElement.closest('[rdxMenuPopup]')?.focus({ preventScroll: true });
|
|
191
544
|
}
|
|
192
|
-
return false;
|
|
193
545
|
}
|
|
194
|
-
|
|
195
|
-
|
|
546
|
+
onItemClick(event) {
|
|
547
|
+
if (this.disabled()) {
|
|
548
|
+
event.preventDefault();
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
this.onSelect.emit();
|
|
552
|
+
if (this.closeOnClick())
|
|
553
|
+
this.rootContext?.close();
|
|
554
|
+
}
|
|
555
|
+
onActivate(event) {
|
|
556
|
+
if (this.disabled()) {
|
|
557
|
+
event.preventDefault();
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
this.onSelect.emit();
|
|
561
|
+
if (this.closeOnClick())
|
|
562
|
+
this.rootContext?.close();
|
|
563
|
+
}
|
|
564
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuLinkItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
565
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuLinkItem, isStandalone: true, selector: "a[rdxMenuLinkItem]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, closeOnClick: { classPropertyName: "closeOnClick", publicName: "closeOnClick", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect" }, host: { attributes: { "role": "menuitem", "tabindex": "-1" }, listeners: { "focus": "onFocus()", "blur": "onBlur()", "pointermove": "onPointerMove($event)", "pointerleave": "onPointerLeave($event)", "click": "onItemClick($event)", "keydown.enter": "onActivate($event)" }, properties: { "attr.data-disabled": "disabled() ? \"\" : undefined", "attr.aria-disabled": "disabled() ? true : undefined", "attr.data-highlighted": "highlighted() ? \"\" : undefined", "attr.data-label": "label() ?? undefined" } }, exportAs: ["rdxMenuLinkItem"], ngImport: i0 }); }
|
|
196
566
|
}
|
|
197
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
567
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuLinkItem, decorators: [{
|
|
198
568
|
type: Directive,
|
|
199
569
|
args: [{
|
|
200
|
-
selector: '[
|
|
570
|
+
selector: 'a[rdxMenuLinkItem]',
|
|
571
|
+
exportAs: 'rdxMenuLinkItem',
|
|
201
572
|
host: {
|
|
202
|
-
|
|
203
|
-
|
|
573
|
+
role: 'menuitem',
|
|
574
|
+
tabindex: '-1',
|
|
575
|
+
'[attr.data-disabled]': 'disabled() ? "" : undefined',
|
|
576
|
+
'[attr.aria-disabled]': 'disabled() ? true : undefined',
|
|
577
|
+
'[attr.data-highlighted]': 'highlighted() ? "" : undefined',
|
|
578
|
+
'[attr.data-label]': 'label() ?? undefined',
|
|
579
|
+
'(focus)': 'onFocus()',
|
|
580
|
+
'(blur)': 'onBlur()',
|
|
581
|
+
'(pointermove)': 'onPointerMove($event)',
|
|
582
|
+
'(pointerleave)': 'onPointerLeave($event)',
|
|
583
|
+
'(click)': 'onItemClick($event)',
|
|
584
|
+
'(keydown.enter)': 'onActivate($event)'
|
|
204
585
|
}
|
|
205
586
|
}]
|
|
206
|
-
}] });
|
|
587
|
+
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], closeOnClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnClick", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }] } });
|
|
207
588
|
|
|
208
|
-
|
|
589
|
+
/** Selector for focusable menu items within the popup. */
|
|
590
|
+
const ITEM_SELECTOR = [
|
|
591
|
+
'[rdxMenuItem]:not([data-disabled])',
|
|
592
|
+
'[rdxMenuCheckboxItem]:not([data-disabled])',
|
|
593
|
+
'[rdxMenuRadioItem]:not([data-disabled])',
|
|
594
|
+
'[rdxMenuLinkItem]:not([data-disabled])',
|
|
595
|
+
'[rdxMenuSubTrigger]:not([data-disabled])'
|
|
596
|
+
].join(',');
|
|
597
|
+
function getFocusableItems(popup) {
|
|
598
|
+
// Exclude items that belong to a nested child popup (submenu).
|
|
599
|
+
return Array.from(popup.querySelectorAll(ITEM_SELECTOR)).filter((item) => item.closest('[rdxMenuPopup]') === popup);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* A container for the menu contents.
|
|
603
|
+
*/
|
|
604
|
+
class RdxMenuPopup {
|
|
209
605
|
constructor() {
|
|
210
|
-
this.
|
|
211
|
-
this.
|
|
212
|
-
this.
|
|
213
|
-
this.
|
|
214
|
-
this.
|
|
215
|
-
this.
|
|
216
|
-
this.
|
|
606
|
+
this.rootContext = injectRdxMenuRootContext();
|
|
607
|
+
this.dismissableLayer = inject(RdxDismissableLayer);
|
|
608
|
+
this.focusScope = inject(RdxFocusScope);
|
|
609
|
+
this.wrapper = inject(RdxPopperContentWrapper, { optional: true });
|
|
610
|
+
this.elementRef = inject(ElementRef);
|
|
611
|
+
this.dismissableLayersContext = inject(RdxDismissableLayersContextToken);
|
|
612
|
+
this.search = '';
|
|
613
|
+
this.align = computed(() => this.wrapper?.placedAlign(), ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
614
|
+
this.side = computed(() => this.wrapper?.placedSide(), ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
615
|
+
/**
|
|
616
|
+
* Event handler called when the escape key is pressed. Can be prevented.
|
|
617
|
+
*/
|
|
618
|
+
this.escapeKeyDown = outputFromObservable(outputToObservable(this.dismissableLayer.escapeKeyDown));
|
|
619
|
+
/**
|
|
620
|
+
* Event handler called when a pointerdown event happens outside of the popup. Can be prevented.
|
|
621
|
+
*/
|
|
622
|
+
this.pointerDownOutside = outputFromObservable(outputToObservable(this.dismissableLayer.pointerDownOutside));
|
|
623
|
+
/**
|
|
624
|
+
* Event handler called when focus moves outside of the popup. Can be prevented.
|
|
625
|
+
*/
|
|
626
|
+
this.focusOutside = outputFromObservable(outputToObservable(this.dismissableLayer.focusOutside));
|
|
627
|
+
/**
|
|
628
|
+
* Event handler called when an interaction happens outside of the popup. Can be prevented.
|
|
629
|
+
*/
|
|
630
|
+
this.interactOutside = outputFromObservable(outputToObservable(this.dismissableLayer.interactOutside));
|
|
631
|
+
/**
|
|
632
|
+
* Event handler called before focus moves into the popup. Can be prevented.
|
|
633
|
+
*/
|
|
634
|
+
this.openAutoFocus = outputFromObservable(outputToObservable(this.focusScope.mountAutoFocus));
|
|
635
|
+
/**
|
|
636
|
+
* Event handler called before focus returns after the popup is removed. Can be prevented.
|
|
637
|
+
*/
|
|
638
|
+
this.closeAutoFocus = outputFromObservable(outputToObservable(this.focusScope.unmountAutoFocus));
|
|
639
|
+
const unregister = this.rootContext.registerTransitionElement(this.elementRef.nativeElement);
|
|
640
|
+
inject(DestroyRef).onDestroy(() => {
|
|
641
|
+
unregister();
|
|
642
|
+
clearTimeout(this.searchTimer);
|
|
643
|
+
});
|
|
644
|
+
effect((onCleanup) => {
|
|
645
|
+
if (!this.rootContext.isSubmenu()) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const element = this.elementRef.nativeElement;
|
|
649
|
+
this.dismissableLayersContext.branches.update((branches) => [...branches, element]);
|
|
650
|
+
onCleanup(() => {
|
|
651
|
+
this.dismissableLayersContext.branches.update((branches) => branches.filter((branch) => branch !== element));
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
this.dismissableLayer.dismiss.subscribe(() => {
|
|
655
|
+
this.rootContext.close();
|
|
656
|
+
});
|
|
657
|
+
// Move focus into the popup when the menu opens — unless the opener suppressed it
|
|
658
|
+
// (e.g. menubar hover-switching, where focus stays on the trigger).
|
|
217
659
|
effect(() => {
|
|
218
|
-
|
|
219
|
-
|
|
660
|
+
const autoFocus = this.rootContext.autoFocus();
|
|
661
|
+
if (this.rootContext.isOpen() && autoFocus) {
|
|
662
|
+
requestAnimationFrame(() => {
|
|
663
|
+
// `'popup'` focuses the container without highlighting an item (pointer opening).
|
|
664
|
+
if (autoFocus === 'popup') {
|
|
665
|
+
this.elementRef.nativeElement.focus({ preventScroll: true });
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const items = getFocusableItems(this.elementRef.nativeElement);
|
|
669
|
+
const item = autoFocus === 'last' ? items[items.length - 1] : items[0];
|
|
670
|
+
item?.focus({ preventScroll: true });
|
|
671
|
+
});
|
|
672
|
+
}
|
|
220
673
|
});
|
|
221
674
|
}
|
|
675
|
+
handleCloseParent(event) {
|
|
676
|
+
event.stopPropagation();
|
|
677
|
+
this.rootContext.close();
|
|
678
|
+
if (this.rootContext.isSubmenu() && this.rootContext.closeParentOnEsc()) {
|
|
679
|
+
this.rootContext.closeParent();
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
handleKeydown(event) {
|
|
683
|
+
const el = this.elementRef.nativeElement;
|
|
684
|
+
const items = getFocusableItems(el);
|
|
685
|
+
const current = document.activeElement;
|
|
686
|
+
const currentIndex = items.indexOf(current);
|
|
687
|
+
switch (event.key) {
|
|
688
|
+
case 'ArrowDown': {
|
|
689
|
+
event.preventDefault();
|
|
690
|
+
event.stopPropagation();
|
|
691
|
+
const atEnd = currentIndex >= items.length - 1;
|
|
692
|
+
const next = atEnd
|
|
693
|
+
? this.rootContext.loopFocus()
|
|
694
|
+
? items[0]
|
|
695
|
+
: items[items.length - 1]
|
|
696
|
+
: items[currentIndex + 1];
|
|
697
|
+
next?.focus({ preventScroll: true });
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
case 'ArrowUp': {
|
|
701
|
+
event.preventDefault();
|
|
702
|
+
event.stopPropagation();
|
|
703
|
+
const atStart = currentIndex <= 0;
|
|
704
|
+
const prev = atStart
|
|
705
|
+
? this.rootContext.loopFocus()
|
|
706
|
+
? items[items.length - 1]
|
|
707
|
+
: items[0]
|
|
708
|
+
: items[currentIndex - 1];
|
|
709
|
+
prev?.focus({ preventScroll: true });
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
case 'Home': {
|
|
713
|
+
event.preventDefault();
|
|
714
|
+
event.stopPropagation();
|
|
715
|
+
items[0]?.focus({ preventScroll: true });
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
case 'End': {
|
|
719
|
+
event.preventDefault();
|
|
720
|
+
event.stopPropagation();
|
|
721
|
+
items[items.length - 1]?.focus({ preventScroll: true });
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
case 'ArrowLeft': {
|
|
725
|
+
const trigger = this.rootContext.trigger();
|
|
726
|
+
if (!trigger?.hasAttribute('rdxMenuSubTrigger')) {
|
|
727
|
+
if (this.rootContext.handlePopupArrowNavigation(-1)) {
|
|
728
|
+
event.preventDefault();
|
|
729
|
+
event.stopPropagation();
|
|
730
|
+
}
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
// Close this popup and return focus to the trigger (used by submenus).
|
|
734
|
+
event.preventDefault();
|
|
735
|
+
event.stopPropagation();
|
|
736
|
+
this.rootContext.close();
|
|
737
|
+
trigger.focus({ preventScroll: true });
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
case 'ArrowRight': {
|
|
741
|
+
if (this.rootContext.handlePopupArrowNavigation(1)) {
|
|
742
|
+
event.preventDefault();
|
|
743
|
+
event.stopPropagation();
|
|
744
|
+
}
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
case 'Escape': {
|
|
748
|
+
event.preventDefault();
|
|
749
|
+
event.stopPropagation();
|
|
750
|
+
this.rootContext.close();
|
|
751
|
+
if (this.rootContext.isSubmenu() && this.rootContext.closeParentOnEsc()) {
|
|
752
|
+
this.rootContext.closeParent();
|
|
753
|
+
}
|
|
754
|
+
this.rootContext.trigger()?.focus({ preventScroll: true });
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
757
|
+
case 'Tab': {
|
|
758
|
+
// Close on tab to allow natural tab navigation
|
|
759
|
+
this.rootContext.close();
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
default: {
|
|
763
|
+
// Typeahead
|
|
764
|
+
if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
765
|
+
event.preventDefault();
|
|
766
|
+
const char = event.key.toLowerCase();
|
|
767
|
+
this.search += char;
|
|
768
|
+
clearTimeout(this.searchTimer);
|
|
769
|
+
this.searchTimer = setTimeout(() => {
|
|
770
|
+
this.search = '';
|
|
771
|
+
this.searchTimer = undefined;
|
|
772
|
+
}, 1000);
|
|
773
|
+
const query = this.search.length > 1 && [...this.search].every((c) => c === char) ? char : this.search;
|
|
774
|
+
const startIndex = currentIndex >= 0 ? currentIndex + 1 : 0;
|
|
775
|
+
const rotated = [...items.slice(startIndex), ...items.slice(0, startIndex)];
|
|
776
|
+
const match = rotated.find((item) => {
|
|
777
|
+
const text = (item.dataset['label'] ?? item.textContent?.trim() ?? '').toLowerCase();
|
|
778
|
+
return text.startsWith(query);
|
|
779
|
+
});
|
|
780
|
+
match?.focus({ preventScroll: true });
|
|
781
|
+
}
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuPopup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
787
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxMenuPopup, isStandalone: true, selector: "[rdxMenuPopup]", outputs: { escapeKeyDown: "escapeKeyDown", pointerDownOutside: "pointerDownOutside", focusOutside: "focusOutside", interactOutside: "interactOutside", openAutoFocus: "openAutoFocus", closeAutoFocus: "closeAutoFocus" }, host: { attributes: { "role": "menu", "tabindex": "-1" }, listeners: { "keydown": "handleKeydown($event)", "rdx-menu-close-parent": "handleCloseParent($event)" }, properties: { "attr.aria-orientation": "rootContext.orientation()", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-state": "rootContext.isOpen() ? \"open\" : \"closed\"", "attr.data-starting-style": "rootContext.transitionStatus() === \"starting\" ? \"\" : undefined", "attr.data-ending-style": "rootContext.transitionStatus() === \"ending\" ? \"\" : undefined", "attr.data-align": "align()", "attr.data-side": "side()" } }, providers: [
|
|
788
|
+
provideRdxDismissableLayerConfig(() => {
|
|
789
|
+
const rootContext = injectRdxMenuRootContext();
|
|
790
|
+
return {
|
|
791
|
+
disableOutsidePointerEvents: computed(() => rootContext.modal())
|
|
792
|
+
};
|
|
793
|
+
}),
|
|
794
|
+
provideRdxFocusScopeConfig(() => ({
|
|
795
|
+
trapped: signal(false)
|
|
796
|
+
}))
|
|
797
|
+
], exportAs: ["rdxMenuPopup"], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxDismissableLayer }, { directive: i3.RdxFocusScope }], ngImport: i0 }); }
|
|
798
|
+
}
|
|
799
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuPopup, decorators: [{
|
|
800
|
+
type: Directive,
|
|
801
|
+
args: [{
|
|
802
|
+
selector: '[rdxMenuPopup]',
|
|
803
|
+
exportAs: 'rdxMenuPopup',
|
|
804
|
+
hostDirectives: [RdxPopperContent, RdxDismissableLayer, RdxFocusScope],
|
|
805
|
+
providers: [
|
|
806
|
+
provideRdxDismissableLayerConfig(() => {
|
|
807
|
+
const rootContext = injectRdxMenuRootContext();
|
|
808
|
+
return {
|
|
809
|
+
disableOutsidePointerEvents: computed(() => rootContext.modal())
|
|
810
|
+
};
|
|
811
|
+
}),
|
|
812
|
+
provideRdxFocusScopeConfig(() => ({
|
|
813
|
+
trapped: signal(false)
|
|
814
|
+
}))
|
|
815
|
+
],
|
|
816
|
+
host: {
|
|
817
|
+
role: 'menu',
|
|
818
|
+
tabindex: '-1',
|
|
819
|
+
'[attr.aria-orientation]': 'rootContext.orientation()',
|
|
820
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
821
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
822
|
+
'[attr.data-state]': 'rootContext.isOpen() ? "open" : "closed"',
|
|
823
|
+
'[attr.data-starting-style]': 'rootContext.transitionStatus() === "starting" ? "" : undefined',
|
|
824
|
+
'[attr.data-ending-style]': 'rootContext.transitionStatus() === "ending" ? "" : undefined',
|
|
825
|
+
'[attr.data-align]': 'align()',
|
|
826
|
+
'[attr.data-side]': 'side()',
|
|
827
|
+
'(keydown)': 'handleKeydown($event)',
|
|
828
|
+
'(rdx-menu-close-parent)': 'handleCloseParent($event)'
|
|
829
|
+
}
|
|
830
|
+
}]
|
|
831
|
+
}], ctorParameters: () => [], propDecorators: { escapeKeyDown: [{ type: i0.Output, args: ["escapeKeyDown"] }], pointerDownOutside: [{ type: i0.Output, args: ["pointerDownOutside"] }], focusOutside: [{ type: i0.Output, args: ["focusOutside"] }], interactOutside: [{ type: i0.Output, args: ["interactOutside"] }], openAutoFocus: [{ type: i0.Output, args: ["openAutoFocus"] }], closeAutoFocus: [{ type: i0.Output, args: ["closeAutoFocus"] }] } });
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Moves the menu to a different part of the DOM.
|
|
835
|
+
* Applied on ng-template — no host bindings (ng-template is not a real DOM node).
|
|
836
|
+
*/
|
|
837
|
+
class RdxMenuPortal {
|
|
838
|
+
constructor() {
|
|
839
|
+
/**
|
|
840
|
+
* Optional container to portal the content into. Defaults to `document.body`.
|
|
841
|
+
*/
|
|
842
|
+
this.container = input(...(ngDevMode ? [undefined, { debugName: "container" }] : /* istanbul ignore next */ []));
|
|
843
|
+
}
|
|
844
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
845
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuPortal, isStandalone: true, selector: "[rdxMenuPortal]", inputs: { container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["rdxMenuPortal"], hostDirectives: [{ directive: i1$1.RdxPortal, inputs: ["container", "container"] }], ngImport: i0 }); }
|
|
846
|
+
}
|
|
847
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuPortal, decorators: [{
|
|
848
|
+
type: Directive,
|
|
849
|
+
args: [{
|
|
850
|
+
selector: '[rdxMenuPortal]',
|
|
851
|
+
exportAs: 'rdxMenuPortal',
|
|
852
|
+
hostDirectives: [
|
|
853
|
+
{
|
|
854
|
+
directive: RdxPortal,
|
|
855
|
+
inputs: ['container']
|
|
856
|
+
}
|
|
857
|
+
]
|
|
858
|
+
}]
|
|
859
|
+
}], propDecorators: { container: [{ type: i0.Input, args: [{ isSignal: true, alias: "container", required: false }] }] } });
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Positions the menu against its trigger.
|
|
863
|
+
*/
|
|
864
|
+
class RdxMenuPositioner {
|
|
865
|
+
constructor() {
|
|
866
|
+
this.rootContext = injectRdxMenuRootContext();
|
|
867
|
+
this.wrapper = inject(RdxPopperContentWrapper);
|
|
868
|
+
/**
|
|
869
|
+
* An element to position the popup against. Defaults to the trigger.
|
|
870
|
+
*/
|
|
871
|
+
this.anchor = input(...(ngDevMode ? [undefined, { debugName: "anchor" }] : /* istanbul ignore next */ []));
|
|
872
|
+
/**
|
|
873
|
+
* The preferred side of the trigger to render against when open.
|
|
874
|
+
*/
|
|
875
|
+
this.side = input('bottom', ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
876
|
+
/**
|
|
877
|
+
* Distance between the trigger and the popup in pixels.
|
|
878
|
+
*/
|
|
879
|
+
this.sideOffset = input(0, { ...(ngDevMode ? { debugName: "sideOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
880
|
+
/**
|
|
881
|
+
* How to align the popup relative to the specified side.
|
|
882
|
+
*/
|
|
883
|
+
this.align = input('start', ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
884
|
+
/**
|
|
885
|
+
* An offset in pixels from the `start` or `end` alignment options.
|
|
886
|
+
*/
|
|
887
|
+
this.alignOffset = input(0, { ...(ngDevMode ? { debugName: "alignOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
888
|
+
/**
|
|
889
|
+
* Minimum distance to maintain between the arrow and the edges of the popup.
|
|
890
|
+
*/
|
|
891
|
+
this.arrowPadding = input(5, { ...(ngDevMode ? { debugName: "arrowPadding" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
892
|
+
/**
|
|
893
|
+
* Whether to override side and alignment preferences to prevent collisions.
|
|
894
|
+
*/
|
|
895
|
+
this.avoidCollisions = input(true, { ...(ngDevMode ? { debugName: "avoidCollisions" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
896
|
+
/**
|
|
897
|
+
* The element used as the collision boundary.
|
|
898
|
+
*/
|
|
899
|
+
this.collisionBoundary = input(...(ngDevMode ? [undefined, { debugName: "collisionBoundary" }] : /* istanbul ignore next */ []));
|
|
900
|
+
/**
|
|
901
|
+
* Distance in pixels from the boundary edges where collision detection should occur.
|
|
902
|
+
*/
|
|
903
|
+
this.collisionPadding = input(5, ...(ngDevMode ? [{ debugName: "collisionPadding" }] : /* istanbul ignore next */ []));
|
|
904
|
+
/**
|
|
905
|
+
* The sticky behavior on the alignment axis.
|
|
906
|
+
*/
|
|
907
|
+
this.sticky = input('partial', ...(ngDevMode ? [{ debugName: "sticky" }] : /* istanbul ignore next */ []));
|
|
908
|
+
/**
|
|
909
|
+
* Whether to hide the popup when the trigger becomes fully occluded.
|
|
910
|
+
*/
|
|
911
|
+
this.hideWhenDetached = input(false, { ...(ngDevMode ? { debugName: "hideWhenDetached" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
912
|
+
/**
|
|
913
|
+
* The CSS position strategy used by Floating UI.
|
|
914
|
+
*/
|
|
915
|
+
this.positionStrategy = input('fixed', ...(ngDevMode ? [{ debugName: "positionStrategy" }] : /* istanbul ignore next */ []));
|
|
916
|
+
/**
|
|
917
|
+
* Whether to update position on every animation frame.
|
|
918
|
+
*/
|
|
919
|
+
this.updatePositionStrategy = input('always', ...(ngDevMode ? [{ debugName: "updatePositionStrategy" }] : /* istanbul ignore next */ []));
|
|
920
|
+
/**
|
|
921
|
+
* Emits when the popup has been placed.
|
|
922
|
+
*/
|
|
923
|
+
this.placed = outputFromObservable(outputToObservable(inject(RdxPopperContentWrapper).placed));
|
|
924
|
+
}
|
|
925
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuPositioner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
926
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuPositioner, isStandalone: true, selector: "[rdxMenuPositioner]", 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 } }, outputs: { placed: "placed" }, 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()", "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 '--radix-menu-content-transform-origin': 'var(--radix-popper-transform-origin)',\n '--radix-menu-content-available-width': 'var(--radix-popper-available-width)',\n '--radix-menu-content-available-height': 'var(--radix-popper-available-height)',\n '--radix-menu-trigger-width': 'var(--radix-popper-anchor-width)',\n '--radix-menu-trigger-height': 'var(--radix-popper-anchor-height)'\n }" } }, providers: [
|
|
927
|
+
provideRdxPopperContentConfig({ arrowPadding: 5, collisionPadding: 5, updatePositionStrategy: 'always' })
|
|
928
|
+
], exportAs: ["rdxMenuPositioner"], 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 }); }
|
|
929
|
+
}
|
|
930
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuPositioner, decorators: [{
|
|
931
|
+
type: Directive,
|
|
932
|
+
args: [{
|
|
933
|
+
selector: '[rdxMenuPositioner]',
|
|
934
|
+
exportAs: 'rdxMenuPositioner',
|
|
935
|
+
providers: [
|
|
936
|
+
provideRdxPopperContentConfig({ arrowPadding: 5, collisionPadding: 5, updatePositionStrategy: 'always' })
|
|
937
|
+
],
|
|
938
|
+
hostDirectives: [
|
|
939
|
+
{
|
|
940
|
+
directive: RdxPopperContentWrapper,
|
|
941
|
+
inputs: [
|
|
942
|
+
'anchor',
|
|
943
|
+
'side',
|
|
944
|
+
'sideOffset',
|
|
945
|
+
'align',
|
|
946
|
+
'alignOffset',
|
|
947
|
+
'arrowPadding',
|
|
948
|
+
'avoidCollisions',
|
|
949
|
+
'collisionBoundary',
|
|
950
|
+
'collisionPadding',
|
|
951
|
+
'sticky',
|
|
952
|
+
'hideWhenDetached',
|
|
953
|
+
'positionStrategy',
|
|
954
|
+
'updatePositionStrategy'
|
|
955
|
+
]
|
|
956
|
+
}
|
|
957
|
+
],
|
|
958
|
+
host: {
|
|
959
|
+
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
960
|
+
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
961
|
+
'[attr.data-anchor-hidden]': 'wrapper.anchorHidden() ? "" : undefined',
|
|
962
|
+
'[attr.data-align]': 'wrapper.placedAlign()',
|
|
963
|
+
'[attr.data-side]': 'wrapper.placedSide()',
|
|
964
|
+
'[style]': `{
|
|
965
|
+
'--anchor-width': 'var(--radix-popper-anchor-width)',
|
|
966
|
+
'--anchor-height': 'var(--radix-popper-anchor-height)',
|
|
967
|
+
'--available-width': 'var(--radix-popper-available-width)',
|
|
968
|
+
'--available-height': 'var(--radix-popper-available-height)',
|
|
969
|
+
'--positioner-width': 'var(--radix-popper-content-wrapper-width)',
|
|
970
|
+
'--positioner-height': 'var(--radix-popper-content-wrapper-height)',
|
|
971
|
+
'--transform-origin': 'var(--radix-popper-transform-origin)',
|
|
972
|
+
'--radix-menu-content-transform-origin': 'var(--radix-popper-transform-origin)',
|
|
973
|
+
'--radix-menu-content-available-width': 'var(--radix-popper-available-width)',
|
|
974
|
+
'--radix-menu-content-available-height': 'var(--radix-popper-available-height)',
|
|
975
|
+
'--radix-menu-trigger-width': 'var(--radix-popper-anchor-width)',
|
|
976
|
+
'--radix-menu-trigger-height': 'var(--radix-popper-anchor-height)'
|
|
977
|
+
}`
|
|
978
|
+
}
|
|
979
|
+
}]
|
|
980
|
+
}], 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 }] }], placed: [{ type: i0.Output, args: ["placed"] }] } });
|
|
981
|
+
|
|
982
|
+
const [injectRdxMenuRadioGroupContext, provideRdxMenuRadioGroupContext] = createContext('RdxMenuRadioGroupContext');
|
|
983
|
+
const radioGroupContextFactory = () => {
|
|
984
|
+
const instance = inject(RdxMenuRadioGroup);
|
|
985
|
+
return {
|
|
986
|
+
value: instance.value,
|
|
987
|
+
selectValue: (v) => instance.selectValue(v)
|
|
988
|
+
};
|
|
989
|
+
};
|
|
990
|
+
/**
|
|
991
|
+
* Groups radio items in a menu.
|
|
992
|
+
*/
|
|
993
|
+
class RdxMenuRadioGroup {
|
|
994
|
+
constructor() {
|
|
995
|
+
/**
|
|
996
|
+
* The currently selected value.
|
|
997
|
+
*/
|
|
998
|
+
this.value = model(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
999
|
+
/**
|
|
1000
|
+
* Emits when the selected value changes.
|
|
1001
|
+
*/
|
|
1002
|
+
this.onValueChange = output();
|
|
1003
|
+
}
|
|
1004
|
+
selectValue(newValue) {
|
|
1005
|
+
this.value.set(newValue);
|
|
1006
|
+
this.onValueChange.emit(newValue);
|
|
1007
|
+
}
|
|
1008
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuRadioGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1009
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuRadioGroup, isStandalone: true, selector: "[rdxMenuRadioGroup]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { attributes: { "role": "group" } }, providers: [provideRdxMenuRadioGroupContext(radioGroupContextFactory)], exportAs: ["rdxMenuRadioGroup"], ngImport: i0 }); }
|
|
1010
|
+
}
|
|
1011
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuRadioGroup, decorators: [{
|
|
1012
|
+
type: Directive,
|
|
1013
|
+
args: [{
|
|
1014
|
+
selector: '[rdxMenuRadioGroup]',
|
|
1015
|
+
exportAs: 'rdxMenuRadioGroup',
|
|
1016
|
+
providers: [provideRdxMenuRadioGroupContext(radioGroupContextFactory)],
|
|
1017
|
+
host: {
|
|
1018
|
+
role: 'group'
|
|
1019
|
+
}
|
|
1020
|
+
}]
|
|
1021
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }] } });
|
|
1022
|
+
|
|
1023
|
+
const [injectRdxMenuRadioItemContext, provideRdxMenuRadioItemContext] = createContext('RdxMenuRadioItemContext');
|
|
1024
|
+
const radioItemContextFactory = () => {
|
|
1025
|
+
const instance = inject(RdxMenuRadioItem);
|
|
1026
|
+
return {
|
|
1027
|
+
checked: instance.checked
|
|
1028
|
+
};
|
|
1029
|
+
};
|
|
1030
|
+
/**
|
|
1031
|
+
* A radio item within a menu radio group.
|
|
1032
|
+
*/
|
|
1033
|
+
class RdxMenuRadioItem {
|
|
1034
|
+
constructor() {
|
|
1035
|
+
this.rootContext = injectRdxMenuRootContext(true);
|
|
1036
|
+
this.radioGroupContext = injectRdxMenuRadioGroupContext();
|
|
1037
|
+
this.elementRef = inject(ElementRef);
|
|
1038
|
+
this.isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
|
|
1039
|
+
/** The value of this radio item. */
|
|
1040
|
+
this.value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1041
|
+
/** Whether this item is disabled. */
|
|
1042
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1043
|
+
/** Whether selecting closes the menu. Defaults to false — radio items stay open. */
|
|
1044
|
+
this.closeOnClick = input(false, { ...(ngDevMode ? { debugName: "closeOnClick" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1045
|
+
/** Explicit typeahead label. When set, overrides textContent for character search. */
|
|
1046
|
+
this.label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
1047
|
+
/** Emits when this item is selected. */
|
|
1048
|
+
this.onSelect = output();
|
|
1049
|
+
this.checked = computed(() => this.radioGroupContext.value() === this.value(), ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
1050
|
+
this.highlighted = computed(() => this.isFocused(), ...(ngDevMode ? [{ debugName: "highlighted" }] : /* istanbul ignore next */ []));
|
|
1051
|
+
this.getCheckedState = getCheckedState;
|
|
1052
|
+
}
|
|
222
1053
|
onFocus() {
|
|
223
1054
|
if (!this.disabled()) {
|
|
224
1055
|
this.isFocused.set(true);
|
|
@@ -228,288 +1059,588 @@ class RdxMenuItemDirective {
|
|
|
228
1059
|
this.isFocused.set(false);
|
|
229
1060
|
}
|
|
230
1061
|
onPointerMove(event) {
|
|
231
|
-
if (event.defaultPrevented)
|
|
1062
|
+
if (event.defaultPrevented || event.pointerType !== 'mouse' || this.disabled()) {
|
|
232
1063
|
return;
|
|
233
|
-
|
|
1064
|
+
}
|
|
1065
|
+
if (this.rootContext && !this.rootContext.highlightItemOnHover()) {
|
|
234
1066
|
return;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
1067
|
+
}
|
|
1068
|
+
if (document.activeElement !== this.elementRef.nativeElement) {
|
|
1069
|
+
this.elementRef.nativeElement.focus({ preventScroll: true });
|
|
238
1070
|
}
|
|
239
1071
|
}
|
|
240
|
-
|
|
241
|
-
|
|
1072
|
+
onPointerLeave(event) {
|
|
1073
|
+
if (event.pointerType !== 'mouse') {
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
if (document.activeElement === this.elementRef.nativeElement) {
|
|
1077
|
+
this.elementRef.nativeElement.closest('[rdxMenuPopup]')?.focus({ preventScroll: true });
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
onItemClick() {
|
|
1081
|
+
if (this.disabled()) {
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
this.selectItem();
|
|
1085
|
+
}
|
|
1086
|
+
onActivate(event) {
|
|
1087
|
+
if (this.disabled()) {
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
event.preventDefault();
|
|
1091
|
+
this.selectItem();
|
|
1092
|
+
}
|
|
1093
|
+
selectItem() {
|
|
1094
|
+
const v = this.value();
|
|
1095
|
+
this.radioGroupContext.selectValue(v);
|
|
1096
|
+
this.onSelect.emit(v);
|
|
1097
|
+
if (this.closeOnClick())
|
|
1098
|
+
this.rootContext?.close();
|
|
1099
|
+
}
|
|
1100
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuRadioItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1101
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuRadioItem, isStandalone: true, selector: "[rdxMenuRadioItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, closeOnClick: { classPropertyName: "closeOnClick", publicName: "closeOnClick", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect" }, host: { attributes: { "role": "menuitemradio", "tabindex": "-1" }, listeners: { "focus": "onFocus()", "blur": "onBlur()", "pointermove": "onPointerMove($event)", "pointerleave": "onPointerLeave($event)", "click": "onItemClick()", "keydown.enter": "onActivate($event)", "keydown.space": "onActivate($event)" }, properties: { "attr.aria-checked": "checked()", "attr.data-state": "getCheckedState(checked())", "attr.data-disabled": "disabled() ? \"\" : undefined", "attr.aria-disabled": "disabled() ? true : undefined", "attr.data-highlighted": "highlighted() ? \"\" : undefined", "attr.data-label": "label() ?? undefined" } }, providers: [provideRdxMenuRadioItemContext(radioItemContextFactory)], exportAs: ["rdxMenuRadioItem"], ngImport: i0 }); }
|
|
242
1102
|
}
|
|
243
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1103
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuRadioItem, decorators: [{
|
|
244
1104
|
type: Directive,
|
|
245
1105
|
args: [{
|
|
246
|
-
selector: '[
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
directive: CdkMenuItem,
|
|
250
|
-
outputs: ['cdkMenuItemTriggered: menuItemTriggered']
|
|
251
|
-
}
|
|
252
|
-
],
|
|
1106
|
+
selector: '[rdxMenuRadioItem]',
|
|
1107
|
+
exportAs: 'rdxMenuRadioItem',
|
|
1108
|
+
providers: [provideRdxMenuRadioItemContext(radioItemContextFactory)],
|
|
253
1109
|
host: {
|
|
254
|
-
role: '
|
|
1110
|
+
role: 'menuitemradio',
|
|
255
1111
|
tabindex: '-1',
|
|
256
|
-
'[attr.
|
|
257
|
-
'[attr.data-state]': '
|
|
258
|
-
'[attr.
|
|
259
|
-
'[attr.
|
|
260
|
-
'[attr.data-highlighted]':
|
|
1112
|
+
'[attr.aria-checked]': 'checked()',
|
|
1113
|
+
'[attr.data-state]': 'getCheckedState(checked())',
|
|
1114
|
+
'[attr.data-disabled]': 'disabled() ? "" : undefined',
|
|
1115
|
+
'[attr.aria-disabled]': 'disabled() ? true : undefined',
|
|
1116
|
+
'[attr.data-highlighted]': 'highlighted() ? "" : undefined',
|
|
1117
|
+
'[attr.data-label]': 'label() ?? undefined',
|
|
261
1118
|
'(focus)': 'onFocus()',
|
|
262
1119
|
'(blur)': 'onBlur()',
|
|
263
|
-
'(pointermove)': 'onPointerMove($event)'
|
|
1120
|
+
'(pointermove)': 'onPointerMove($event)',
|
|
1121
|
+
'(pointerleave)': 'onPointerLeave($event)',
|
|
1122
|
+
'(click)': 'onItemClick()',
|
|
1123
|
+
'(keydown.enter)': 'onActivate($event)',
|
|
1124
|
+
'(keydown.space)': 'onActivate($event)'
|
|
264
1125
|
}
|
|
265
1126
|
}]
|
|
266
|
-
}],
|
|
1127
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], closeOnClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnClick", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }] } });
|
|
267
1128
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
1129
|
+
/**
|
|
1130
|
+
* Renders when the parent radio item is selected.
|
|
1131
|
+
* Set `keepMounted` to keep the element in the DOM when unselected (enables CSS animations).
|
|
1132
|
+
*/
|
|
1133
|
+
class RdxMenuRadioItemIndicator {
|
|
1134
|
+
constructor() {
|
|
1135
|
+
this.itemContext = injectRdxMenuRadioItemContext();
|
|
1136
|
+
/** Keep the indicator in the DOM when unselected so CSS exit animations can play. */
|
|
1137
|
+
this.keepMounted = input(false, { ...(ngDevMode ? { debugName: "keepMounted" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1138
|
+
this.dataState = computed(() => getCheckedState(this.itemContext.checked()), ...(ngDevMode ? [{ debugName: "dataState" }] : /* istanbul ignore next */ []));
|
|
1139
|
+
this.isVisible = computed(() => this.itemContext.checked() === true, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
|
|
1140
|
+
}
|
|
1141
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuRadioItemIndicator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1142
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuRadioItemIndicator, isStandalone: true, selector: "[rdxMenuRadioItemIndicator]", inputs: { keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-state": "dataState()", "attr.data-starting-style": "isVisible() ? \"\" : undefined", "attr.data-ending-style": "!isVisible() ? \"\" : undefined", "style.display": "!keepMounted() && !isVisible() ? \"none\" : null" } }, exportAs: ["rdxMenuRadioItemIndicator"], ngImport: i0 }); }
|
|
271
1143
|
}
|
|
272
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1144
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuRadioItemIndicator, decorators: [{
|
|
273
1145
|
type: Directive,
|
|
274
1146
|
args: [{
|
|
275
|
-
selector: '[
|
|
1147
|
+
selector: '[rdxMenuRadioItemIndicator]',
|
|
1148
|
+
exportAs: 'rdxMenuRadioItemIndicator',
|
|
1149
|
+
host: {
|
|
1150
|
+
'[attr.data-state]': 'dataState()',
|
|
1151
|
+
'[attr.data-starting-style]': 'isVisible() ? "" : undefined',
|
|
1152
|
+
'[attr.data-ending-style]': '!isVisible() ? "" : undefined',
|
|
1153
|
+
'[style.display]': '!keepMounted() && !isVisible() ? "none" : null'
|
|
1154
|
+
}
|
|
276
1155
|
}]
|
|
277
|
-
}] });
|
|
1156
|
+
}], propDecorators: { keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }] } });
|
|
278
1157
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
1158
|
+
/**
|
|
1159
|
+
* A visual separator between groups of menu items.
|
|
1160
|
+
*/
|
|
1161
|
+
class RdxMenuSeparator {
|
|
1162
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuSeparator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1163
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxMenuSeparator, isStandalone: true, selector: "[rdxMenuSeparator]", host: { attributes: { "role": "separator" }, properties: { "attr.aria-orientation": "\"horizontal\"" } }, exportAs: ["rdxMenuSeparator"], ngImport: i0 }); }
|
|
282
1164
|
}
|
|
283
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1165
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuSeparator, decorators: [{
|
|
284
1166
|
type: Directive,
|
|
285
1167
|
args: [{
|
|
286
|
-
selector: '[
|
|
287
|
-
|
|
1168
|
+
selector: '[rdxMenuSeparator]',
|
|
1169
|
+
exportAs: 'rdxMenuSeparator',
|
|
1170
|
+
host: {
|
|
1171
|
+
role: 'separator',
|
|
1172
|
+
'[attr.aria-orientation]': '"horizontal"'
|
|
1173
|
+
}
|
|
288
1174
|
}]
|
|
289
1175
|
}] });
|
|
290
1176
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
1177
|
+
const numberOrUndefined$1 = (value) => (value == null ? undefined : numberAttribute(value));
|
|
1178
|
+
const submenuRootsByTrigger = new WeakMap();
|
|
1179
|
+
/**
|
|
1180
|
+
* An item inside a parent menu that opens a nested submenu.
|
|
1181
|
+
*
|
|
1182
|
+
* Place this inside `ng-container rdxMenuRoot` that wraps both the trigger
|
|
1183
|
+
* and the submenu positioner. The inner root provides the submenu context;
|
|
1184
|
+
* the outer popup discovers this element via `[rdxMenuSubTrigger]` in its
|
|
1185
|
+
* ITEM_SELECTOR and includes it in keyboard navigation.
|
|
1186
|
+
*/
|
|
1187
|
+
class RdxMenuSubTrigger {
|
|
1188
|
+
constructor() {
|
|
1189
|
+
this.submenuContext = injectRdxMenuRootContext();
|
|
1190
|
+
this.submenuRoot = inject(RdxMenuRoot);
|
|
1191
|
+
this.elementRef = inject(ElementRef);
|
|
1192
|
+
this.destroyRef = inject(DestroyRef);
|
|
1193
|
+
this.isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
|
|
1194
|
+
/** Whether this trigger (and therefore the submenu) is disabled. */
|
|
1195
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1196
|
+
/** Whether this trigger should be treated as a native button. Auto-detected for `<button>`. */
|
|
1197
|
+
this.nativeButton = input(false, { ...(ngDevMode ? { debugName: "nativeButton" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1198
|
+
/** Whether hovering the trigger opens the submenu. */
|
|
1199
|
+
this.openOnHover = input(true, { ...(ngDevMode ? { debugName: "openOnHover" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1200
|
+
/** Delay before hover opens the submenu, in milliseconds. */
|
|
1201
|
+
this.delay = input(100, { ...(ngDevMode ? { debugName: "delay" } : /* istanbul ignore next */ {}), transform: numberOrUndefined$1 });
|
|
1202
|
+
/** Delay before a pending hover close runs, in milliseconds. */
|
|
1203
|
+
this.closeDelay = input(0, { ...(ngDevMode ? { debugName: "closeDelay" } : /* istanbul ignore next */ {}), transform: numberOrUndefined$1 });
|
|
1204
|
+
/** Explicit typeahead label. When set, overrides textContent for character search. */
|
|
1205
|
+
this.label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
1206
|
+
/** Highlighted when focused OR while the submenu is open. */
|
|
1207
|
+
this.highlighted = computed(() => this.isFocused() || this.submenuContext.isOpen(), ...(ngDevMode ? [{ debugName: "highlighted" }] : /* istanbul ignore next */ []));
|
|
1208
|
+
this.nativeButtonState = computed(() => this.nativeButton() || this.elementRef.nativeElement.tagName === 'BUTTON', ...(ngDevMode ? [{ debugName: "nativeButtonState" }] : /* istanbul ignore next */ []));
|
|
1209
|
+
this.submenuContext.markAsSubmenu();
|
|
1210
|
+
effect((onCleanup) => {
|
|
1211
|
+
const el = this.elementRef.nativeElement;
|
|
1212
|
+
const unregister = this.submenuContext.registerTrigger(el);
|
|
1213
|
+
submenuRootsByTrigger.set(el, this.submenuRoot);
|
|
1214
|
+
onCleanup(() => {
|
|
1215
|
+
unregister();
|
|
1216
|
+
submenuRootsByTrigger.delete(el);
|
|
1217
|
+
});
|
|
1218
|
+
});
|
|
1219
|
+
this.destroyRef.onDestroy(() => {
|
|
1220
|
+
clearTimeout(this.openTimer);
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
onFocus() {
|
|
1224
|
+
if (!this.disabled()) {
|
|
1225
|
+
this.clearSiblingHighlights();
|
|
1226
|
+
this.isFocused.set(true);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
onBlur() {
|
|
1230
|
+
this.isFocused.set(false);
|
|
1231
|
+
}
|
|
1232
|
+
onClick() {
|
|
1233
|
+
if (this.disabled())
|
|
1234
|
+
return;
|
|
1235
|
+
this.clearSiblingHighlights();
|
|
1236
|
+
if (!this.submenuContext.isOpen()) {
|
|
1237
|
+
this.closeSiblingSubmenus();
|
|
1238
|
+
}
|
|
1239
|
+
this.submenuContext.toggle();
|
|
1240
|
+
}
|
|
1241
|
+
onArrowRight(event) {
|
|
1242
|
+
if (this.disabled())
|
|
1243
|
+
return;
|
|
1244
|
+
event.preventDefault();
|
|
1245
|
+
event.stopPropagation();
|
|
1246
|
+
this.clearSiblingHighlights();
|
|
1247
|
+
if (!this.submenuContext.isOpen()) {
|
|
1248
|
+
this.closeSiblingSubmenus();
|
|
1249
|
+
this.submenuContext.show();
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
onPointerMove(event) {
|
|
1253
|
+
if (event.pointerType !== 'mouse' || this.disabled() || !this.openOnHover())
|
|
1254
|
+
return;
|
|
1255
|
+
this.clearSiblingHighlights();
|
|
1256
|
+
if (this.submenuContext.highlightItemOnHover() && document.activeElement !== this.elementRef.nativeElement) {
|
|
1257
|
+
this.elementRef.nativeElement.focus({ preventScroll: true });
|
|
1258
|
+
}
|
|
1259
|
+
if (!this.submenuContext.isOpen()) {
|
|
1260
|
+
clearTimeout(this.openTimer);
|
|
1261
|
+
this.closeSiblingSubmenus();
|
|
1262
|
+
this.openTimer = setTimeout(() => {
|
|
1263
|
+
this.submenuContext.show(false);
|
|
1264
|
+
}, this.delay() ?? 100);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
onPointerLeave() {
|
|
1268
|
+
clearTimeout(this.openTimer);
|
|
1269
|
+
}
|
|
1270
|
+
clearHighlight() {
|
|
1271
|
+
this.isFocused.set(false);
|
|
1272
|
+
}
|
|
1273
|
+
closeSiblingSubmenus() {
|
|
1274
|
+
const currentTrigger = this.elementRef.nativeElement;
|
|
1275
|
+
const parentPopup = currentTrigger.closest('[rdxMenuPopup]');
|
|
1276
|
+
if (!parentPopup)
|
|
1277
|
+
return;
|
|
1278
|
+
parentPopup.querySelectorAll('[rdxMenuSubTrigger]').forEach((trigger) => {
|
|
1279
|
+
if (trigger === currentTrigger || trigger.closest('[rdxMenuPopup]') !== parentPopup) {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
submenuRootsByTrigger.get(trigger)?.close();
|
|
1283
|
+
trigger.dispatchEvent(new CustomEvent('rdx-menu-subtrigger-clear-highlight'));
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
clearSiblingHighlights() {
|
|
1287
|
+
const currentTrigger = this.elementRef.nativeElement;
|
|
1288
|
+
const parentPopup = currentTrigger.closest('[rdxMenuPopup]');
|
|
1289
|
+
if (!parentPopup)
|
|
1290
|
+
return;
|
|
1291
|
+
parentPopup.querySelectorAll('[rdxMenuSubTrigger]').forEach((trigger) => {
|
|
1292
|
+
if (trigger === currentTrigger || trigger.closest('[rdxMenuPopup]') !== parentPopup) {
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
trigger.dispatchEvent(new CustomEvent('rdx-menu-subtrigger-clear-highlight'));
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuSubTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1299
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuSubTrigger, isStandalone: true, selector: "[rdxMenuSubTrigger]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, nativeButton: { classPropertyName: "nativeButton", publicName: "nativeButton", isSignal: true, isRequired: false, transformFunction: null }, openOnHover: { classPropertyName: "openOnHover", publicName: "openOnHover", 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 }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "menuitem", "tabindex": "-1" }, listeners: { "focus": "onFocus()", "blur": "onBlur()", "click": "onClick()", "keydown.arrowright": "onArrowRight($event)", "pointermove": "onPointerMove($event)", "pointerleave": "onPointerLeave()", "rdx-menu-subtrigger-clear-highlight": "clearHighlight()" }, properties: { "attr.type": "nativeButtonState() ? \"button\" : undefined", "attr.aria-haspopup": "\"menu\"", "attr.aria-expanded": "submenuContext.isOpen()", "attr.aria-disabled": "disabled() ? true : undefined", "attr.disabled": "nativeButtonState() && disabled() ? \"\" : undefined", "attr.data-state": "submenuContext.isOpen() ? \"open\" : \"closed\"", "attr.data-popup-open": "submenuContext.isOpen() ? \"\" : undefined", "attr.data-highlighted": "highlighted() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined", "attr.data-label": "label() ?? undefined" } }, exportAs: ["rdxMenuSubTrigger"], hostDirectives: [{ directive: i1.RdxPopperAnchor }, { directive: i2.RdxDismissableLayerBranch }], ngImport: i0 }); }
|
|
294
1300
|
}
|
|
295
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1301
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuSubTrigger, decorators: [{
|
|
296
1302
|
type: Directive,
|
|
297
1303
|
args: [{
|
|
298
|
-
selector: '[
|
|
1304
|
+
selector: '[rdxMenuSubTrigger]',
|
|
1305
|
+
exportAs: 'rdxMenuSubTrigger',
|
|
1306
|
+
hostDirectives: [RdxPopperAnchor, RdxDismissableLayerBranch],
|
|
299
1307
|
host: {
|
|
300
|
-
|
|
301
|
-
|
|
1308
|
+
'[attr.type]': 'nativeButtonState() ? "button" : undefined',
|
|
1309
|
+
role: 'menuitem',
|
|
1310
|
+
tabindex: '-1',
|
|
1311
|
+
'[attr.aria-haspopup]': '"menu"',
|
|
1312
|
+
'[attr.aria-expanded]': 'submenuContext.isOpen()',
|
|
1313
|
+
'[attr.aria-disabled]': 'disabled() ? true : undefined',
|
|
1314
|
+
'[attr.disabled]': 'nativeButtonState() && disabled() ? "" : undefined',
|
|
1315
|
+
'[attr.data-state]': 'submenuContext.isOpen() ? "open" : "closed"',
|
|
1316
|
+
'[attr.data-popup-open]': 'submenuContext.isOpen() ? "" : undefined',
|
|
1317
|
+
'[attr.data-highlighted]': 'highlighted() ? "" : undefined',
|
|
1318
|
+
'[attr.data-disabled]': 'disabled() ? "" : undefined',
|
|
1319
|
+
'[attr.data-label]': 'label() ?? undefined',
|
|
1320
|
+
'(focus)': 'onFocus()',
|
|
1321
|
+
'(blur)': 'onBlur()',
|
|
1322
|
+
'(click)': 'onClick()',
|
|
1323
|
+
'(keydown.arrowright)': 'onArrowRight($event)',
|
|
1324
|
+
'(pointermove)': 'onPointerMove($event)',
|
|
1325
|
+
'(pointerleave)': 'onPointerLeave()',
|
|
1326
|
+
'(rdx-menu-subtrigger-clear-highlight)': 'clearHighlight()'
|
|
302
1327
|
}
|
|
303
1328
|
}]
|
|
304
|
-
}] });
|
|
1329
|
+
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], nativeButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "nativeButton", required: false }] }], openOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "openOnHover", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], closeDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeDelay", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
305
1330
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
1331
|
+
const numberOrUndefined = (value) => (value == null ? undefined : numberAttribute(value));
|
|
1332
|
+
/**
|
|
1333
|
+
* A button that opens the menu.
|
|
1334
|
+
*/
|
|
1335
|
+
class RdxMenuTrigger {
|
|
1336
|
+
constructor() {
|
|
1337
|
+
this.rootContext = injectRdxMenuRootContext();
|
|
1338
|
+
this.elementRef = inject(ElementRef);
|
|
1339
|
+
this.destroyRef = inject(DestroyRef);
|
|
1340
|
+
this.dismissableLayersContext = inject(RdxDismissableLayersContextToken);
|
|
1341
|
+
/** Whether this trigger should be treated as a native button. Auto-detected for `<button>`. */
|
|
1342
|
+
this.nativeButton = input(false, { ...(ngDevMode ? { debugName: "nativeButton" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1343
|
+
/** Whether this trigger is disabled. */
|
|
1344
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1345
|
+
/** Whether hovering the trigger opens the menu. */
|
|
1346
|
+
this.openOnHover = input(false, { ...(ngDevMode ? { debugName: "openOnHover" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1347
|
+
/** Delay before hover opens the menu, in milliseconds. */
|
|
1348
|
+
this.delay = input(undefined, { ...(ngDevMode ? { debugName: "delay" } : /* istanbul ignore next */ {}), transform: numberOrUndefined });
|
|
1349
|
+
/** Delay before hover leave closes the menu, in milliseconds. */
|
|
1350
|
+
this.closeDelay = input(undefined, { ...(ngDevMode ? { debugName: "closeDelay" } : /* istanbul ignore next */ {}), transform: numberOrUndefined });
|
|
1351
|
+
this.nativeButtonState = computed(() => this.nativeButton() || this.elementRef.nativeElement.tagName === 'BUTTON', ...(ngDevMode ? [{ debugName: "nativeButtonState" }] : /* istanbul ignore next */ []));
|
|
1352
|
+
this.isDisabled = computed(() => this.rootContext.disabled() || this.disabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
1353
|
+
effect((onCleanup) => {
|
|
1354
|
+
const el = this.elementRef.nativeElement;
|
|
1355
|
+
const unregister = this.rootContext.registerTrigger(el);
|
|
1356
|
+
onCleanup(unregister);
|
|
1357
|
+
});
|
|
1358
|
+
// When a coordinator (e.g. the menubar) drives this trigger, hover-switching focuses the
|
|
1359
|
+
// trigger and opens the popup without pulling focus inside it. Register the trigger as a
|
|
1360
|
+
// dismissable-layer branch so that focus/pointer interactions on it are treated as "inside"
|
|
1361
|
+
// and do not dismiss the just-opened popup.
|
|
1362
|
+
effect((onCleanup) => {
|
|
1363
|
+
if (!this.rootContext.hasTriggerInteractionHandler()) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
const el = this.elementRef.nativeElement;
|
|
1367
|
+
this.dismissableLayersContext.branches.update((branches) => [...branches, el]);
|
|
1368
|
+
onCleanup(() => {
|
|
1369
|
+
this.dismissableLayersContext.branches.update((branches) => branches.filter((b) => b !== el));
|
|
1370
|
+
});
|
|
1371
|
+
});
|
|
1372
|
+
this.destroyRef.onDestroy(() => {
|
|
1373
|
+
this.clearOpenTimer();
|
|
1374
|
+
this.clearCloseTimer();
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
handleClick() {
|
|
1378
|
+
if (this.isDisabled()) {
|
|
1379
|
+
return;
|
|
342
1380
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (side === 'top' || side === 'bottom') {
|
|
346
|
-
originX = 'start';
|
|
347
|
-
overlayX = 'start';
|
|
348
|
-
offsetX = alignOffset;
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
351
|
-
originY = 'top';
|
|
352
|
-
overlayY = 'top';
|
|
353
|
-
offsetY = alignOffset;
|
|
354
|
-
}
|
|
355
|
-
break;
|
|
356
|
-
case 'end':
|
|
357
|
-
if (side === 'top' || side === 'bottom') {
|
|
358
|
-
originX = 'end';
|
|
359
|
-
overlayX = 'end';
|
|
360
|
-
offsetX = -alignOffset;
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
originY = 'bottom';
|
|
364
|
-
overlayY = 'bottom';
|
|
365
|
-
offsetY = -alignOffset;
|
|
366
|
-
}
|
|
367
|
-
break;
|
|
368
|
-
case 'center':
|
|
369
|
-
default:
|
|
370
|
-
if (side === 'top' || side === 'bottom') {
|
|
371
|
-
originX = 'center';
|
|
372
|
-
overlayX = 'center';
|
|
373
|
-
}
|
|
374
|
-
else {
|
|
375
|
-
originY = 'center';
|
|
376
|
-
overlayY = 'center';
|
|
377
|
-
}
|
|
378
|
-
break;
|
|
1381
|
+
if (this.rootContext.handleTriggerInteraction({ type: 'click' })) {
|
|
1382
|
+
return;
|
|
379
1383
|
}
|
|
380
|
-
|
|
381
|
-
originX,
|
|
382
|
-
originY,
|
|
383
|
-
overlayX,
|
|
384
|
-
overlayY,
|
|
385
|
-
offsetX,
|
|
386
|
-
offsetY
|
|
387
|
-
};
|
|
1384
|
+
this.rootContext.toggle();
|
|
388
1385
|
}
|
|
389
|
-
|
|
390
|
-
this.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
this.align = input(...(ngDevMode ? [undefined, { debugName: "align" }] : []));
|
|
397
|
-
/**
|
|
398
|
-
* @description The distance in pixels from the trigger.
|
|
399
|
-
*/
|
|
400
|
-
this.sideOffset = input(NaN, ...(ngDevMode ? [{ debugName: "sideOffset", transform: numberAttribute }] : [{
|
|
401
|
-
transform: numberAttribute
|
|
402
|
-
}]));
|
|
403
|
-
/**
|
|
404
|
-
* @description An offset in pixels from the "start" or "end" alignment options.
|
|
405
|
-
*/
|
|
406
|
-
this.alignOffset = input(NaN, ...(ngDevMode ? [{ debugName: "alignOffset", transform: numberAttribute }] : [{
|
|
407
|
-
transform: numberAttribute
|
|
408
|
-
}]));
|
|
409
|
-
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{
|
|
410
|
-
transform: booleanAttribute
|
|
411
|
-
}]));
|
|
412
|
-
this.enablePositions = false;
|
|
413
|
-
// TODO
|
|
414
|
-
this.positions = computed(() => this.computePositions(), ...(ngDevMode ? [{ debugName: "positions" }] : []));
|
|
415
|
-
this.onMenuPositionEffect();
|
|
416
|
-
}
|
|
417
|
-
/** @ignore */
|
|
418
|
-
onPointerDown($event) {
|
|
419
|
-
// only call handler if it's the left button (mousedown gets triggered by all mouse buttons)
|
|
420
|
-
// but not when the control key is pressed (avoiding MacOS right click)
|
|
421
|
-
if (!this.disabled() && $event.button === 0 && !$event.ctrlKey) {
|
|
422
|
-
/* empty */
|
|
423
|
-
if (!this.cdkTrigger.isOpen()) {
|
|
424
|
-
// prevent trigger focusing when opening
|
|
425
|
-
// this allows the content to be given focus without competition
|
|
426
|
-
$event.preventDefault();
|
|
427
|
-
}
|
|
1386
|
+
handleArrowDown(event) {
|
|
1387
|
+
if (this.rootContext.handleTriggerInteraction({ type: 'arrowdown', event })) {
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
event.preventDefault();
|
|
1391
|
+
if (!this.isDisabled() && !this.rootContext.isOpen()) {
|
|
1392
|
+
this.rootContext.show('first');
|
|
428
1393
|
}
|
|
429
1394
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
1395
|
+
handleArrowUp(event) {
|
|
1396
|
+
if (this.rootContext.handleTriggerInteraction({ type: 'arrowup', event })) {
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
event.preventDefault();
|
|
1400
|
+
if (!this.isDisabled() && !this.rootContext.isOpen()) {
|
|
1401
|
+
this.rootContext.show('last');
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
handleArrowLeft(event) {
|
|
1405
|
+
this.rootContext.handleTriggerInteraction({ type: 'arrowleft', event });
|
|
1406
|
+
}
|
|
1407
|
+
handleArrowRight(event) {
|
|
1408
|
+
this.rootContext.handleTriggerInteraction({ type: 'arrowright', event });
|
|
1409
|
+
}
|
|
1410
|
+
handleHome(event) {
|
|
1411
|
+
this.rootContext.handleTriggerInteraction({ type: 'home', event });
|
|
1412
|
+
}
|
|
1413
|
+
handleEnd(event) {
|
|
1414
|
+
this.rootContext.handleTriggerInteraction({ type: 'end', event });
|
|
1415
|
+
}
|
|
1416
|
+
handleEscape(event) {
|
|
1417
|
+
this.rootContext.handleTriggerInteraction({ type: 'escape', event });
|
|
1418
|
+
}
|
|
1419
|
+
handleKeyboardToggle(event) {
|
|
1420
|
+
if (this.nativeButtonState()) {
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
event.preventDefault();
|
|
1424
|
+
this.handleClick();
|
|
1425
|
+
}
|
|
1426
|
+
handlePointerEnter(event) {
|
|
1427
|
+
if (this.rootContext.handleTriggerInteraction({ type: 'pointerenter', event })) {
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
if (event.pointerType === 'touch' || !this.openOnHover() || this.isDisabled()) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
this.clearCloseTimer();
|
|
1434
|
+
this.clearOpenTimer();
|
|
1435
|
+
const delay = this.delay() ?? 0;
|
|
1436
|
+
if (delay <= 0) {
|
|
1437
|
+
this.rootContext.show();
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
this.openTimer = setTimeout(() => {
|
|
1441
|
+
this.openTimer = undefined;
|
|
1442
|
+
this.rootContext.show();
|
|
1443
|
+
}, delay);
|
|
1444
|
+
}
|
|
1445
|
+
handlePointerLeave(event) {
|
|
1446
|
+
if (event.pointerType === 'touch' || !this.openOnHover()) {
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
this.clearOpenTimer();
|
|
1450
|
+
this.clearCloseTimer();
|
|
1451
|
+
const closeDelay = this.closeDelay() ?? 0;
|
|
1452
|
+
if (closeDelay <= 0) {
|
|
1453
|
+
this.rootContext.close();
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
this.closeTimer = setTimeout(() => {
|
|
1457
|
+
this.closeTimer = undefined;
|
|
1458
|
+
this.rootContext.close();
|
|
1459
|
+
}, closeDelay);
|
|
1460
|
+
}
|
|
1461
|
+
clearOpenTimer() {
|
|
1462
|
+
clearTimeout(this.openTimer);
|
|
1463
|
+
this.openTimer = undefined;
|
|
1464
|
+
}
|
|
1465
|
+
clearCloseTimer() {
|
|
1466
|
+
clearTimeout(this.closeTimer);
|
|
1467
|
+
this.closeTimer = undefined;
|
|
1468
|
+
}
|
|
1469
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1470
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxMenuTrigger, isStandalone: true, selector: "[rdxMenuTrigger]", inputs: { nativeButton: { classPropertyName: "nativeButton", publicName: "nativeButton", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, openOnHover: { classPropertyName: "openOnHover", publicName: "openOnHover", 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 } }, host: { listeners: { "click": "handleClick()", "pointerenter": "handlePointerEnter($event)", "pointerleave": "handlePointerLeave($event)", "keydown.arrowdown": "handleArrowDown($event)", "keydown.arrowup": "handleArrowUp($event)", "keydown.arrowleft": "handleArrowLeft($event)", "keydown.arrowright": "handleArrowRight($event)", "keydown.home": "handleHome($event)", "keydown.end": "handleEnd($event)", "keydown.escape": "handleEscape($event)", "keydown.enter": "handleKeyboardToggle($event)", "keydown.space": "handleKeyboardToggle($event)" }, properties: { "attr.type": "nativeButtonState() ? \"button\" : undefined", "attr.role": "rootContext.hasTriggerInteractionHandler() ? \"menuitem\" : nativeButtonState() ? undefined : \"button\"", "attr.tabindex": "rootContext.hasTriggerInteractionHandler() ? \"-1\" : undefined", "attr.aria-haspopup": "\"menu\"", "attr.aria-expanded": "rootContext.isOpen()", "attr.aria-disabled": "isDisabled() ? true : undefined", "attr.disabled": "nativeButtonState() && isDisabled() ? \"\" : undefined", "attr.data-state": "rootContext.isOpen() ? \"open\" : \"closed\"", "attr.data-disabled": "isDisabled() ? \"\" : undefined", "attr.data-popup-open": "rootContext.isOpen() ? \"\" : undefined" } }, exportAs: ["rdxMenuTrigger"], hostDirectives: [{ directive: i1.RdxPopperAnchor }], ngImport: i0 }); }
|
|
1471
|
+
}
|
|
1472
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuTrigger, decorators: [{
|
|
1473
|
+
type: Directive,
|
|
1474
|
+
args: [{
|
|
1475
|
+
selector: '[rdxMenuTrigger]',
|
|
1476
|
+
exportAs: 'rdxMenuTrigger',
|
|
1477
|
+
hostDirectives: [RdxPopperAnchor],
|
|
1478
|
+
host: {
|
|
1479
|
+
'[attr.type]': 'nativeButtonState() ? "button" : undefined',
|
|
1480
|
+
'[attr.role]': 'rootContext.hasTriggerInteractionHandler() ? "menuitem" : nativeButtonState() ? undefined : "button"',
|
|
1481
|
+
'[attr.tabindex]': 'rootContext.hasTriggerInteractionHandler() ? "-1" : undefined',
|
|
1482
|
+
'[attr.aria-haspopup]': '"menu"',
|
|
1483
|
+
'[attr.aria-expanded]': 'rootContext.isOpen()',
|
|
1484
|
+
'[attr.aria-disabled]': 'isDisabled() ? true : undefined',
|
|
1485
|
+
'[attr.disabled]': 'nativeButtonState() && isDisabled() ? "" : undefined',
|
|
1486
|
+
'[attr.data-state]': 'rootContext.isOpen() ? "open" : "closed"',
|
|
1487
|
+
'[attr.data-disabled]': 'isDisabled() ? "" : undefined',
|
|
1488
|
+
'[attr.data-popup-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
1489
|
+
'(click)': 'handleClick()',
|
|
1490
|
+
'(pointerenter)': 'handlePointerEnter($event)',
|
|
1491
|
+
'(pointerleave)': 'handlePointerLeave($event)',
|
|
1492
|
+
'(keydown.arrowdown)': 'handleArrowDown($event)',
|
|
1493
|
+
'(keydown.arrowup)': 'handleArrowUp($event)',
|
|
1494
|
+
'(keydown.arrowleft)': 'handleArrowLeft($event)',
|
|
1495
|
+
'(keydown.arrowright)': 'handleArrowRight($event)',
|
|
1496
|
+
'(keydown.home)': 'handleHome($event)',
|
|
1497
|
+
'(keydown.end)': 'handleEnd($event)',
|
|
1498
|
+
'(keydown.escape)': 'handleEscape($event)',
|
|
1499
|
+
'(keydown.enter)': 'handleKeyboardToggle($event)',
|
|
1500
|
+
'(keydown.space)': 'handleKeyboardToggle($event)'
|
|
1501
|
+
}
|
|
1502
|
+
}]
|
|
1503
|
+
}], ctorParameters: () => [], propDecorators: { nativeButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "nativeButton", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], openOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "openOnHover", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], closeDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeDelay", required: false }] }] } });
|
|
1504
|
+
|
|
1505
|
+
/**
|
|
1506
|
+
* A viewport that smoothly animates the popup size when its content changes
|
|
1507
|
+
* (e.g. switching menubar menus of different sizes, or expanding a section).
|
|
1508
|
+
*
|
|
1509
|
+
* It measures its content with a `ResizeObserver` and exposes the current size
|
|
1510
|
+
* as `--popup-width` / `--popup-height` CSS variables on the host. Drive the
|
|
1511
|
+
* animation from the consumer side, for example:
|
|
1512
|
+
*
|
|
1513
|
+
* ```css
|
|
1514
|
+
* [rdxMenuPopup] {
|
|
1515
|
+
* width: var(--popup-width);
|
|
1516
|
+
* height: var(--popup-height);
|
|
1517
|
+
* transition: width 200ms, height 200ms;
|
|
1518
|
+
* }
|
|
1519
|
+
* ```
|
|
1520
|
+
*
|
|
1521
|
+
* `data-transitioning` is present while a size change is in flight.
|
|
1522
|
+
*/
|
|
1523
|
+
class RdxMenuViewport {
|
|
1524
|
+
constructor() {
|
|
1525
|
+
this.elementRef = inject(ElementRef);
|
|
1526
|
+
this.destroyRef = inject(DestroyRef);
|
|
1527
|
+
this.width = signal(undefined, ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
1528
|
+
this.height = signal(undefined, ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
1529
|
+
this.transitioning = signal(false, ...(ngDevMode ? [{ debugName: "transitioning" }] : /* istanbul ignore next */ []));
|
|
1530
|
+
afterNextRender(() => {
|
|
1531
|
+
const el = this.elementRef.nativeElement;
|
|
1532
|
+
// Seed the initial size without marking a transition.
|
|
1533
|
+
this.width.set(el.offsetWidth);
|
|
1534
|
+
this.height.set(el.offsetHeight);
|
|
1535
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
this.observer = new ResizeObserver((entries) => {
|
|
1539
|
+
const entry = entries[0];
|
|
1540
|
+
if (!entry) {
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
const nextWidth = Math.round(entry.contentRect.width);
|
|
1544
|
+
const nextHeight = Math.round(entry.contentRect.height);
|
|
1545
|
+
if (nextWidth === this.width() && nextHeight === this.height()) {
|
|
1546
|
+
return;
|
|
436
1547
|
}
|
|
1548
|
+
this.width.set(nextWidth);
|
|
1549
|
+
this.height.set(nextHeight);
|
|
1550
|
+
this.markTransitioning();
|
|
437
1551
|
});
|
|
1552
|
+
this.observer.observe(el);
|
|
438
1553
|
});
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
this.cdkTrigger.menuPosition = positions;
|
|
443
|
-
this.fireNgOnChanges('menuPosition', this.cdkTrigger.menuPosition, prevMenuPosition);
|
|
444
|
-
}
|
|
445
|
-
fireNgOnChanges(input, currentValue, previousValue, firstChange = false) {
|
|
446
|
-
this.cdkTrigger.ngOnChanges({
|
|
447
|
-
[input]: new SimpleChange(previousValue, currentValue, firstChange)
|
|
1554
|
+
this.destroyRef.onDestroy(() => {
|
|
1555
|
+
this.observer?.disconnect();
|
|
1556
|
+
clearTimeout(this.transitionTimer);
|
|
448
1557
|
});
|
|
449
1558
|
}
|
|
450
|
-
|
|
451
|
-
|
|
1559
|
+
markTransitioning() {
|
|
1560
|
+
this.transitioning.set(true);
|
|
1561
|
+
clearTimeout(this.transitionTimer);
|
|
1562
|
+
const duration = getMaxTransitionDuration(this.elementRef.nativeElement);
|
|
1563
|
+
this.transitionTimer = setTimeout(() => this.transitioning.set(false), duration > 0 ? duration + 50 : 0);
|
|
1564
|
+
}
|
|
1565
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuViewport, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1566
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxMenuViewport, isStandalone: true, selector: "[rdxMenuViewport]", host: { properties: { "attr.data-transitioning": "transitioning() ? \"\" : undefined", "style.--popup-width.px": "width()", "style.--popup-height.px": "height()" } }, exportAs: ["rdxMenuViewport"], ngImport: i0 }); }
|
|
452
1567
|
}
|
|
453
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1568
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuViewport, decorators: [{
|
|
454
1569
|
type: Directive,
|
|
455
1570
|
args: [{
|
|
456
|
-
selector: '[
|
|
457
|
-
|
|
458
|
-
{
|
|
459
|
-
directive: CdkMenuTrigger,
|
|
460
|
-
inputs: ['cdkMenuTriggerFor: menuTriggerFor', 'cdkMenuPosition: menuPosition']
|
|
461
|
-
}
|
|
462
|
-
],
|
|
1571
|
+
selector: '[rdxMenuViewport]',
|
|
1572
|
+
exportAs: 'rdxMenuViewport',
|
|
463
1573
|
host: {
|
|
464
|
-
|
|
465
|
-
'[
|
|
466
|
-
'[
|
|
467
|
-
'[attr.data-state]': "cdkTrigger.isOpen() ? 'open': 'closed'",
|
|
468
|
-
'[attr.data-disabled]': "disabled() ? '' : undefined",
|
|
469
|
-
'(pointerdown)': 'onPointerDown($event)'
|
|
1574
|
+
'[attr.data-transitioning]': 'transitioning() ? "" : undefined',
|
|
1575
|
+
'[style.--popup-width.px]': 'width()',
|
|
1576
|
+
'[style.--popup-height.px]': 'height()'
|
|
470
1577
|
}
|
|
471
1578
|
}]
|
|
472
1579
|
}], ctorParameters: () => [] });
|
|
473
1580
|
|
|
474
1581
|
const menuImports = [
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
1582
|
+
RdxMenuRoot,
|
|
1583
|
+
RdxMenuTrigger,
|
|
1584
|
+
RdxMenuSubTrigger,
|
|
1585
|
+
RdxMenuPortal,
|
|
1586
|
+
RdxMenuPositioner,
|
|
1587
|
+
RdxMenuPopup,
|
|
1588
|
+
RdxMenuViewport,
|
|
1589
|
+
RdxMenuBackdrop,
|
|
1590
|
+
RdxMenuArrow,
|
|
1591
|
+
RdxMenuItem,
|
|
1592
|
+
RdxMenuLinkItem,
|
|
1593
|
+
RdxMenuGroup,
|
|
1594
|
+
RdxMenuGroupLabel,
|
|
1595
|
+
RdxMenuSeparator,
|
|
1596
|
+
RdxMenuCheckboxItem,
|
|
1597
|
+
RdxMenuCheckboxItemIndicator,
|
|
1598
|
+
RdxMenuRadioGroup,
|
|
1599
|
+
RdxMenuRadioItem,
|
|
1600
|
+
RdxMenuRadioItemIndicator
|
|
486
1601
|
];
|
|
487
1602
|
class RdxMenuModule {
|
|
488
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
489
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
1603
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1604
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuModule, imports: [RdxMenuRoot,
|
|
1605
|
+
RdxMenuTrigger,
|
|
1606
|
+
RdxMenuSubTrigger,
|
|
1607
|
+
RdxMenuPortal,
|
|
1608
|
+
RdxMenuPositioner,
|
|
1609
|
+
RdxMenuPopup,
|
|
1610
|
+
RdxMenuViewport,
|
|
1611
|
+
RdxMenuBackdrop,
|
|
1612
|
+
RdxMenuArrow,
|
|
1613
|
+
RdxMenuItem,
|
|
1614
|
+
RdxMenuLinkItem,
|
|
1615
|
+
RdxMenuGroup,
|
|
1616
|
+
RdxMenuGroupLabel,
|
|
1617
|
+
RdxMenuSeparator,
|
|
1618
|
+
RdxMenuCheckboxItem,
|
|
1619
|
+
RdxMenuCheckboxItemIndicator,
|
|
1620
|
+
RdxMenuRadioGroup,
|
|
1621
|
+
RdxMenuRadioItem,
|
|
1622
|
+
RdxMenuRadioItemIndicator], exports: [RdxMenuRoot,
|
|
1623
|
+
RdxMenuTrigger,
|
|
1624
|
+
RdxMenuSubTrigger,
|
|
1625
|
+
RdxMenuPortal,
|
|
1626
|
+
RdxMenuPositioner,
|
|
1627
|
+
RdxMenuPopup,
|
|
1628
|
+
RdxMenuViewport,
|
|
1629
|
+
RdxMenuBackdrop,
|
|
1630
|
+
RdxMenuArrow,
|
|
1631
|
+
RdxMenuItem,
|
|
1632
|
+
RdxMenuLinkItem,
|
|
1633
|
+
RdxMenuGroup,
|
|
1634
|
+
RdxMenuGroupLabel,
|
|
1635
|
+
RdxMenuSeparator,
|
|
1636
|
+
RdxMenuCheckboxItem,
|
|
1637
|
+
RdxMenuCheckboxItemIndicator,
|
|
1638
|
+
RdxMenuRadioGroup,
|
|
1639
|
+
RdxMenuRadioItem,
|
|
1640
|
+
RdxMenuRadioItemIndicator] }); }
|
|
1641
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuModule }); }
|
|
511
1642
|
}
|
|
512
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1643
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxMenuModule, decorators: [{
|
|
513
1644
|
type: NgModule,
|
|
514
1645
|
args: [{
|
|
515
1646
|
imports: [...menuImports],
|
|
@@ -521,5 +1652,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
|
|
|
521
1652
|
* Generated bundle index. Do not edit.
|
|
522
1653
|
*/
|
|
523
1654
|
|
|
524
|
-
export {
|
|
1655
|
+
export { RdxMenuArrow, RdxMenuBackdrop, RdxMenuCheckboxItem, RdxMenuCheckboxItemIndicator, RdxMenuGroup, RdxMenuGroupLabel, RdxMenuItem, RdxMenuLinkItem, RdxMenuModule, RdxMenuPopup, RdxMenuPortal, RdxMenuPositioner, RdxMenuRadioGroup, RdxMenuRadioItem, RdxMenuRadioItemIndicator, RdxMenuRoot, RdxMenuSeparator, RdxMenuSubTrigger, RdxMenuTrigger, RdxMenuViewport, getCheckedState, injectRdxMenuCheckboxItemContext, injectRdxMenuRadioGroupContext, injectRdxMenuRadioItemContext, injectRdxMenuRootContext, isIndeterminate, provideRdxMenuCheckboxItemContext, provideRdxMenuRadioGroupContext, provideRdxMenuRadioItemContext, provideRdxMenuRootContext };
|
|
525
1656
|
//# sourceMappingURL=radix-ng-primitives-menu.mjs.map
|