ng-primitives 0.115.2 → 0.117.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.
@@ -0,0 +1,612 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, input, booleanAttribute, Directive, output, computed, Injector, ViewContainerRef, signal } from '@angular/core';
3
+ import { provideFocusTrapState } from 'ng-primitives/focus-trap';
4
+ import { ngpMenu, provideMenuState, ngpMenuItem, provideMenuItemState, ngpMenuItemCheckbox, provideMenuItemCheckboxState, injectMenuItemCheckboxState, injectMenuItemRadioState, ngpMenuItemRadio, provideMenuItemRadioState, ngpMenuItemRadioGroup, provideMenuItemRadioGroupState, ngpSubmenuTrigger, provideSubmenuTriggerState, NgpSubmenuTriggerStateToken, NgpMenuTriggerStateToken, NgpSubmenuTrigger, provideMenuTriggerState } from 'ng-primitives/menu';
5
+ import { ngpRovingFocusGroup, provideRovingFocusGroupState, ngpRovingFocusItem, provideRovingFocusItemState } from 'ng-primitives/roving-focus';
6
+ import { injectElementRef } from 'ng-primitives/internal';
7
+ import { dataBinding, createPrimitive, controlled, styleBinding, listener, onDestroy } from 'ng-primitives/state';
8
+ import { coerceOffset, coerceFlip, createOverlay, coerceShift } from 'ng-primitives/portal';
9
+
10
+ const defaultContextMenuConfig = {
11
+ offset: 2,
12
+ flip: true,
13
+ container: 'body',
14
+ scrollBehavior: 'close',
15
+ shift: undefined,
16
+ };
17
+ const NgpContextMenuConfigToken = new InjectionToken('NgpContextMenuConfigToken');
18
+ /**
19
+ * Provide the default Context Menu configuration
20
+ * @param config The Context Menu configuration
21
+ * @returns The provider
22
+ */
23
+ function provideContextMenuConfig(config) {
24
+ return [
25
+ {
26
+ provide: NgpContextMenuConfigToken,
27
+ useValue: { ...defaultContextMenuConfig, ...config },
28
+ },
29
+ ];
30
+ }
31
+ /**
32
+ * Inject the Context Menu configuration
33
+ * @returns The global Context Menu configuration
34
+ */
35
+ function injectContextMenuConfig() {
36
+ return inject(NgpContextMenuConfigToken, { optional: true }) ?? defaultContextMenuConfig;
37
+ }
38
+
39
+ /**
40
+ * The `NgpContextMenu` is a container for context menu items.
41
+ */
42
+ class NgpContextMenu {
43
+ constructor() {
44
+ /**
45
+ * Whether focus should wrap around when reaching the end of the menu.
46
+ * @default true
47
+ */
48
+ this.wrap = input(true, ...(ngDevMode ? [{ debugName: "wrap", alias: 'ngpContextMenuWrap',
49
+ transform: booleanAttribute }] : [{
50
+ alias: 'ngpContextMenuWrap',
51
+ transform: booleanAttribute,
52
+ }]));
53
+ this.state = ngpMenu({ wrap: this.wrap });
54
+ ngpRovingFocusGroup({ inherit: false, wrap: this.wrap });
55
+ }
56
+ /** @internal Close the menu and any parent menus */
57
+ closeAllMenus(origin) {
58
+ this.state.closeAllMenus(origin);
59
+ }
60
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenu, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
61
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpContextMenu, isStandalone: true, selector: "[ngpContextMenu]", inputs: { wrap: { classPropertyName: "wrap", publicName: "ngpContextMenuWrap", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
62
+ provideRovingFocusGroupState({ inherit: false }),
63
+ provideMenuState({ inherit: false }),
64
+ provideFocusTrapState(),
65
+ ], exportAs: ["ngpContextMenu"], ngImport: i0 }); }
66
+ }
67
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenu, decorators: [{
68
+ type: Directive,
69
+ args: [{
70
+ selector: '[ngpContextMenu]',
71
+ exportAs: 'ngpContextMenu',
72
+ providers: [
73
+ provideRovingFocusGroupState({ inherit: false }),
74
+ provideMenuState({ inherit: false }),
75
+ provideFocusTrapState(),
76
+ ],
77
+ }]
78
+ }], ctorParameters: () => [], propDecorators: { wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuWrap", required: false }] }] } });
79
+
80
+ /**
81
+ * The `NgpContextMenuItem` directive represents a context menu item.
82
+ */
83
+ class NgpContextMenuItem {
84
+ constructor() {
85
+ /** Whether the menu item is disabled */
86
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpContextMenuItemDisabled',
87
+ transform: booleanAttribute }] : [{
88
+ alias: 'ngpContextMenuItemDisabled',
89
+ transform: booleanAttribute,
90
+ }]));
91
+ /** Whether the menu should close when this item is selected */
92
+ this.closeOnSelect = input(true, ...(ngDevMode ? [{ debugName: "closeOnSelect", alias: 'ngpContextMenuItemCloseOnSelect',
93
+ transform: booleanAttribute }] : [{
94
+ alias: 'ngpContextMenuItemCloseOnSelect',
95
+ transform: booleanAttribute,
96
+ }]));
97
+ ngpMenuItem({ disabled: this.disabled, closeOnSelect: this.closeOnSelect });
98
+ ngpRovingFocusItem({ disabled: this.disabled });
99
+ }
100
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
101
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpContextMenuItem, isStandalone: true, selector: "[ngpContextMenuItem]", inputs: { disabled: { classPropertyName: "disabled", publicName: "ngpContextMenuItemDisabled", isSignal: true, isRequired: false, transformFunction: null }, closeOnSelect: { classPropertyName: "closeOnSelect", publicName: "ngpContextMenuItemCloseOnSelect", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideMenuItemState(), provideRovingFocusItemState()], exportAs: ["ngpContextMenuItem"], ngImport: i0 }); }
102
+ }
103
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItem, decorators: [{
104
+ type: Directive,
105
+ args: [{
106
+ selector: '[ngpContextMenuItem]',
107
+ exportAs: 'ngpContextMenuItem',
108
+ providers: [provideMenuItemState(), provideRovingFocusItemState()],
109
+ }]
110
+ }], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuItemDisabled", required: false }] }], closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuItemCloseOnSelect", required: false }] }] } });
111
+
112
+ /**
113
+ * The `NgpContextMenuItemCheckbox` directive represents a context menu item that can be toggled on and off.
114
+ */
115
+ class NgpContextMenuItemCheckbox {
116
+ constructor() {
117
+ /** Whether the checkbox is checked */
118
+ this.checked = input(false, ...(ngDevMode ? [{ debugName: "checked", alias: 'ngpContextMenuItemCheckboxChecked',
119
+ transform: booleanAttribute }] : [{
120
+ alias: 'ngpContextMenuItemCheckboxChecked',
121
+ transform: booleanAttribute,
122
+ }]));
123
+ /** Event emitted when the checked state changes */
124
+ this.checkedChange = output({
125
+ alias: 'ngpContextMenuItemCheckboxCheckedChange',
126
+ });
127
+ /** Whether the menu item checkbox is disabled */
128
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpContextMenuItemCheckboxDisabled',
129
+ transform: booleanAttribute }] : [{
130
+ alias: 'ngpContextMenuItemCheckboxDisabled',
131
+ transform: booleanAttribute,
132
+ }]));
133
+ ngpMenuItemCheckbox({
134
+ checked: this.checked,
135
+ disabled: this.disabled,
136
+ onCheckedChange: value => this.checkedChange.emit(value),
137
+ });
138
+ ngpRovingFocusItem({ disabled: this.disabled });
139
+ }
140
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItemCheckbox, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
141
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpContextMenuItemCheckbox, isStandalone: true, selector: "[ngpContextMenuItemCheckbox]", inputs: { checked: { classPropertyName: "checked", publicName: "ngpContextMenuItemCheckboxChecked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpContextMenuItemCheckboxDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checkedChange: "ngpContextMenuItemCheckboxCheckedChange" }, providers: [provideMenuItemCheckboxState(), provideRovingFocusItemState()], exportAs: ["ngpContextMenuItemCheckbox"], ngImport: i0 }); }
142
+ }
143
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItemCheckbox, decorators: [{
144
+ type: Directive,
145
+ args: [{
146
+ selector: '[ngpContextMenuItemCheckbox]',
147
+ exportAs: 'ngpContextMenuItemCheckbox',
148
+ providers: [provideMenuItemCheckboxState(), provideRovingFocusItemState()],
149
+ }]
150
+ }], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuItemCheckboxChecked", required: false }] }], checkedChange: [{ type: i0.Output, args: ["ngpContextMenuItemCheckboxCheckedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuItemCheckboxDisabled", required: false }] }] } });
151
+
152
+ /**
153
+ * The `NgpContextMenuItemIndicator` directive renders inside a checkbox or radio context menu item
154
+ * and exposes `data-checked` based on the parent item's checked state.
155
+ */
156
+ class NgpContextMenuItemIndicator {
157
+ constructor() {
158
+ const element = injectElementRef();
159
+ const checkboxState = injectMenuItemCheckboxState({ optional: true });
160
+ const radioState = injectMenuItemRadioState({ optional: true });
161
+ const checked = computed(() => checkboxState()?.checked() ?? radioState()?.checked() ?? false, ...(ngDevMode ? [{ debugName: "checked" }] : []));
162
+ dataBinding(element, 'data-checked', checked);
163
+ }
164
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItemIndicator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
165
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: NgpContextMenuItemIndicator, isStandalone: true, selector: "[ngpContextMenuItemIndicator]", exportAs: ["ngpContextMenuItemIndicator"], ngImport: i0 }); }
166
+ }
167
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItemIndicator, decorators: [{
168
+ type: Directive,
169
+ args: [{
170
+ selector: '[ngpContextMenuItemIndicator]',
171
+ exportAs: 'ngpContextMenuItemIndicator',
172
+ }]
173
+ }], ctorParameters: () => [] });
174
+
175
+ /**
176
+ * The `NgpContextMenuItemRadio` directive represents a radio context menu item within a radio group.
177
+ */
178
+ class NgpContextMenuItemRadio {
179
+ constructor() {
180
+ /** The value this radio item represents */
181
+ this.value = input.required(...(ngDevMode ? [{ debugName: "value", alias: 'ngpContextMenuItemRadioValue' }] : [{
182
+ alias: 'ngpContextMenuItemRadioValue',
183
+ }]));
184
+ /** Whether the radio item is disabled */
185
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpContextMenuItemRadioDisabled',
186
+ transform: booleanAttribute }] : [{
187
+ alias: 'ngpContextMenuItemRadioDisabled',
188
+ transform: booleanAttribute,
189
+ }]));
190
+ ngpMenuItemRadio({ value: this.value, disabled: this.disabled });
191
+ ngpRovingFocusItem({ disabled: this.disabled });
192
+ }
193
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItemRadio, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
194
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpContextMenuItemRadio, isStandalone: true, selector: "[ngpContextMenuItemRadio]", inputs: { value: { classPropertyName: "value", publicName: "ngpContextMenuItemRadioValue", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpContextMenuItemRadioDisabled", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideMenuItemRadioState(), provideRovingFocusItemState()], exportAs: ["ngpContextMenuItemRadio"], ngImport: i0 }); }
195
+ }
196
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItemRadio, decorators: [{
197
+ type: Directive,
198
+ args: [{
199
+ selector: '[ngpContextMenuItemRadio]',
200
+ exportAs: 'ngpContextMenuItemRadio',
201
+ providers: [provideMenuItemRadioState(), provideRovingFocusItemState()],
202
+ }]
203
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuItemRadioValue", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuItemRadioDisabled", required: false }] }] } });
204
+
205
+ /**
206
+ * The `NgpContextMenuItemRadioGroup` directive represents a group of radio context menu items.
207
+ */
208
+ class NgpContextMenuItemRadioGroup {
209
+ constructor() {
210
+ /** The current value of the radio group */
211
+ this.value = input(null, ...(ngDevMode ? [{ debugName: "value", alias: 'ngpContextMenuItemRadioGroupValue' }] : [{
212
+ alias: 'ngpContextMenuItemRadioGroupValue',
213
+ }]));
214
+ /** Event emitted when the value changes */
215
+ this.valueChange = output({
216
+ alias: 'ngpContextMenuItemRadioGroupValueChange',
217
+ });
218
+ ngpMenuItemRadioGroup({
219
+ value: this.value,
220
+ onValueChange: value => this.valueChange.emit(value),
221
+ });
222
+ }
223
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItemRadioGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
224
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpContextMenuItemRadioGroup, isStandalone: true, selector: "[ngpContextMenuItemRadioGroup]", inputs: { value: { classPropertyName: "value", publicName: "ngpContextMenuItemRadioGroupValue", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "ngpContextMenuItemRadioGroupValueChange" }, providers: [provideMenuItemRadioGroupState()], exportAs: ["ngpContextMenuItemRadioGroup"], ngImport: i0 }); }
225
+ }
226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuItemRadioGroup, decorators: [{
227
+ type: Directive,
228
+ args: [{
229
+ selector: '[ngpContextMenuItemRadioGroup]',
230
+ exportAs: 'ngpContextMenuItemRadioGroup',
231
+ providers: [provideMenuItemRadioGroupState()],
232
+ }]
233
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuItemRadioGroupValue", required: false }] }], valueChange: [{ type: i0.Output, args: ["ngpContextMenuItemRadioGroupValueChange"] }] } });
234
+
235
+ /**
236
+ * The `NgpContextMenuSubmenuTrigger` directive turns a context menu item into a submenu trigger.
237
+ */
238
+ class NgpContextMenuSubmenuTrigger {
239
+ constructor() {
240
+ /**
241
+ * Access the submenu template ref.
242
+ */
243
+ this.menu = input(undefined, ...(ngDevMode ? [{ debugName: "menu", alias: 'ngpContextMenuSubmenuTrigger' }] : [{
244
+ alias: 'ngpContextMenuSubmenuTrigger',
245
+ }]));
246
+ /**
247
+ * Define if the trigger should be disabled.
248
+ * @default false
249
+ */
250
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpContextMenuSubmenuTriggerDisabled',
251
+ transform: booleanAttribute }] : [{
252
+ alias: 'ngpContextMenuSubmenuTriggerDisabled',
253
+ transform: booleanAttribute,
254
+ }]));
255
+ /**
256
+ * Define the placement of the submenu relative to the trigger.
257
+ * @default 'right-start'
258
+ */
259
+ this.placement = input('right-start', ...(ngDevMode ? [{ debugName: "placement", alias: 'ngpContextMenuSubmenuTriggerPlacement' }] : [{
260
+ alias: 'ngpContextMenuSubmenuTriggerPlacement',
261
+ }]));
262
+ /**
263
+ * Define the offset of the submenu relative to the trigger.
264
+ * @default 0
265
+ */
266
+ this.offset = input(0, ...(ngDevMode ? [{ debugName: "offset", alias: 'ngpContextMenuSubmenuTriggerOffset',
267
+ transform: coerceOffset }] : [{
268
+ alias: 'ngpContextMenuSubmenuTriggerOffset',
269
+ transform: coerceOffset,
270
+ }]));
271
+ /**
272
+ * Define whether the submenu should flip when there is not enough space.
273
+ * @default true
274
+ */
275
+ this.flip = input(true, ...(ngDevMode ? [{ debugName: "flip", alias: 'ngpContextMenuSubmenuTriggerFlip',
276
+ transform: coerceFlip }] : [{
277
+ alias: 'ngpContextMenuSubmenuTriggerFlip',
278
+ transform: coerceFlip,
279
+ }]));
280
+ this.state = ngpSubmenuTrigger({
281
+ disabled: this.disabled,
282
+ menu: this.menu,
283
+ placement: this.placement,
284
+ offset: this.offset,
285
+ flip: this.flip,
286
+ });
287
+ }
288
+ show() {
289
+ this.state.show();
290
+ }
291
+ hide(origin = 'program') {
292
+ this.state.hide(origin);
293
+ }
294
+ toggle(event) {
295
+ this.state.toggle(event);
296
+ }
297
+ focus(origin = 'program') {
298
+ this.state.focus(origin);
299
+ }
300
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuSubmenuTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
301
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpContextMenuSubmenuTrigger, isStandalone: true, selector: "[ngpContextMenuSubmenuTrigger]", inputs: { menu: { classPropertyName: "menu", publicName: "ngpContextMenuSubmenuTrigger", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpContextMenuSubmenuTriggerDisabled", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "ngpContextMenuSubmenuTriggerPlacement", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "ngpContextMenuSubmenuTriggerOffset", isSignal: true, isRequired: false, transformFunction: null }, flip: { classPropertyName: "flip", publicName: "ngpContextMenuSubmenuTriggerFlip", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
302
+ provideSubmenuTriggerState(),
303
+ { provide: NgpMenuTriggerStateToken, useExisting: NgpSubmenuTriggerStateToken },
304
+ // Provide as NgpSubmenuTrigger so ngpMenuItem can detect this is a submenu trigger
305
+ { provide: NgpSubmenuTrigger, useExisting: NgpContextMenuSubmenuTrigger },
306
+ ], exportAs: ["ngpContextMenuSubmenuTrigger"], ngImport: i0 }); }
307
+ }
308
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuSubmenuTrigger, decorators: [{
309
+ type: Directive,
310
+ args: [{
311
+ selector: '[ngpContextMenuSubmenuTrigger]',
312
+ exportAs: 'ngpContextMenuSubmenuTrigger',
313
+ providers: [
314
+ provideSubmenuTriggerState(),
315
+ { provide: NgpMenuTriggerStateToken, useExisting: NgpSubmenuTriggerStateToken },
316
+ // Provide as NgpSubmenuTrigger so ngpMenuItem can detect this is a submenu trigger
317
+ { provide: NgpSubmenuTrigger, useExisting: NgpContextMenuSubmenuTrigger },
318
+ ],
319
+ }]
320
+ }], propDecorators: { menu: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuSubmenuTrigger", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuSubmenuTriggerDisabled", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuSubmenuTriggerPlacement", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuSubmenuTriggerOffset", required: false }] }], flip: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuSubmenuTriggerFlip", required: false }] }] } });
321
+
322
+ /** Duration in ms before a long-press fires. */
323
+ const LONG_PRESS_DURATION = 700;
324
+ /** Movement threshold in px to cancel a long-press. */
325
+ const LONG_PRESS_MOVE_THRESHOLD = 10;
326
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-function
327
+ const noop = () => { };
328
+ const [NgpContextMenuTriggerStateToken, ngpContextMenuTrigger, _injectContextMenuTriggerState, provideContextMenuTriggerState,] = createPrimitive('NgpContextMenuTrigger', ({ disabled: _disabled = signal(false), menu: _menu = signal(undefined), offset: _offset = signal(2), flip: _flip = signal(true), context: _context = signal(undefined), container, scrollBehavior, shift, }) => {
329
+ const element = injectElementRef();
330
+ const injector = inject(Injector);
331
+ const viewContainerRef = inject(ViewContainerRef);
332
+ // Controlled properties
333
+ const menu = controlled(_menu);
334
+ const disabled = controlled(_disabled);
335
+ const flip = controlled(_flip);
336
+ const offset = controlled(_offset);
337
+ const context = controlled(_context);
338
+ // Internal state
339
+ const overlay = signal(null, ...(ngDevMode ? [{ debugName: "overlay" }] : []));
340
+ const open = computed(() => overlay()?.isOpen() ?? false, ...(ngDevMode ? [{ debugName: "open" }] : []));
341
+ const openOrigin = signal('program', ...(ngDevMode ? [{ debugName: "openOrigin" }] : []));
342
+ const cursorPosition = signal(null, ...(ngDevMode ? [{ debugName: "cursorPosition" }] : []));
343
+ // Host bindings
344
+ dataBinding(element, 'data-open', open);
345
+ // Prevent iOS text selection and callout on long-press.
346
+ // Only apply when the trigger is enabled so disabled triggers retain native behavior.
347
+ styleBinding(element, '-webkit-touch-callout', () => (disabled?.() ? null : 'none'));
348
+ // Long-press state
349
+ let longPressTimer = null;
350
+ let longPressStartX = 0;
351
+ let longPressStartY = 0;
352
+ let longPressTriggered = false;
353
+ // Event listeners
354
+ listener(element, 'contextmenu', onContextMenu);
355
+ listener(element, 'pointerdown', onPointerDown);
356
+ listener(element, 'pointermove', onPointerMove);
357
+ listener(element, 'pointerup', onPointerUp);
358
+ listener(element, 'pointercancel', onPointerCancel);
359
+ function onContextMenu(event) {
360
+ if (disabled?.()) {
361
+ return;
362
+ }
363
+ event.preventDefault();
364
+ // If long-press already triggered, don't re-open
365
+ if (longPressTriggered) {
366
+ longPressTriggered = false;
367
+ return;
368
+ }
369
+ cursorPosition.set({ x: event.clientX, y: event.clientY });
370
+ openOrigin.set('mouse');
371
+ if (open()) {
372
+ // Already open - reposition by updating cursor position and triggering reposition
373
+ overlay()?.updatePosition();
374
+ }
375
+ else {
376
+ showMenu();
377
+ }
378
+ }
379
+ function onPointerDown(event) {
380
+ if (event.pointerType !== 'touch' || disabled?.()) {
381
+ return;
382
+ }
383
+ longPressTriggered = false;
384
+ longPressStartX = event.clientX;
385
+ longPressStartY = event.clientY;
386
+ longPressTimer = setTimeout(() => {
387
+ longPressTriggered = true;
388
+ cursorPosition.set({ x: longPressStartX, y: longPressStartY });
389
+ openOrigin.set('touch');
390
+ showMenu();
391
+ }, LONG_PRESS_DURATION);
392
+ }
393
+ function onPointerMove(event) {
394
+ if (event.pointerType !== 'touch' || !longPressTimer) {
395
+ return;
396
+ }
397
+ const dx = event.clientX - longPressStartX;
398
+ const dy = event.clientY - longPressStartY;
399
+ if (Math.sqrt(dx * dx + dy * dy) > LONG_PRESS_MOVE_THRESHOLD) {
400
+ cancelLongPress();
401
+ }
402
+ }
403
+ function onPointerUp(event) {
404
+ if (event.pointerType !== 'touch') {
405
+ return;
406
+ }
407
+ cancelLongPress();
408
+ }
409
+ function onPointerCancel(event) {
410
+ if (event.pointerType !== 'touch') {
411
+ return;
412
+ }
413
+ cancelLongPress();
414
+ }
415
+ function cancelLongPress() {
416
+ if (longPressTimer) {
417
+ clearTimeout(longPressTimer);
418
+ longPressTimer = null;
419
+ }
420
+ longPressTriggered = false;
421
+ }
422
+ onDestroy(() => cancelLongPress());
423
+ function showMenu() {
424
+ if (!overlay()) {
425
+ createOverlayInstance();
426
+ }
427
+ overlay()?.show();
428
+ }
429
+ function show(origin = 'program') {
430
+ openOrigin.set(origin);
431
+ showMenu();
432
+ }
433
+ function hide(origin = 'program') {
434
+ const currentOverlay = overlay();
435
+ if (!currentOverlay) {
436
+ return;
437
+ }
438
+ currentOverlay.hide({ origin });
439
+ }
440
+ function createOverlayInstance() {
441
+ const menuContent = menu?.();
442
+ if (!menuContent) {
443
+ throw new Error('Context menu must be either a TemplateRef or a ComponentType');
444
+ }
445
+ const config = {
446
+ content: menuContent,
447
+ triggerElement: element.nativeElement,
448
+ viewContainerRef,
449
+ injector,
450
+ context,
451
+ container: container?.(),
452
+ offset: offset(),
453
+ flip: flip(),
454
+ shift,
455
+ placement: signal('right-start'),
456
+ closeOnOutsideClick: true,
457
+ closeOnEscape: true,
458
+ treatTriggerClickAsOutside: true,
459
+ restoreFocus: false,
460
+ scrollBehaviour: scrollBehavior?.() ?? 'close',
461
+ overlayType: 'menu',
462
+ position: cursorPosition,
463
+ trackPosition: true,
464
+ };
465
+ overlay.set(createOverlay(config));
466
+ }
467
+ function setPointerOverContent(_isOver) {
468
+ // No-op for context menu
469
+ }
470
+ // Set the compat state on NgpMenuTriggerStateToken so NgpMenu can find it.
471
+ // NgpMenu injects this token and calls hide(), openOrigin(), setPointerOverContent() on it.
472
+ const menuTriggerToken = inject(NgpMenuTriggerStateToken, { optional: true });
473
+ // eslint-disable-next-line @angular-eslint/no-uncalled-signals -- checking inject() nullability, not signal value
474
+ if (menuTriggerToken) {
475
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
476
+ menuTriggerToken.set({
477
+ menu,
478
+ placement: signal('bottom-start'),
479
+ open,
480
+ offset,
481
+ disabled,
482
+ flip,
483
+ context,
484
+ openOrigin,
485
+ setDisabled: noop,
486
+ setFlip: noop,
487
+ setPlacement: noop,
488
+ setOffset: noop,
489
+ setContext: noop,
490
+ show,
491
+ hide,
492
+ toggle: noop,
493
+ setMenu: noop,
494
+ setPointerOverContent,
495
+ });
496
+ }
497
+ return {
498
+ open,
499
+ openOrigin,
500
+ show,
501
+ hide,
502
+ setPointerOverContent,
503
+ };
504
+ });
505
+ function injectContextMenuTriggerState() {
506
+ return _injectContextMenuTriggerState();
507
+ }
508
+
509
+ /**
510
+ * The `NgpContextMenuTrigger` directive allows you to turn an element into a context menu trigger.
511
+ * Right-clicking or long-pressing the element will open the specified menu at the cursor position.
512
+ */
513
+ class NgpContextMenuTrigger {
514
+ constructor() {
515
+ /**
516
+ * Access the global context menu configuration.
517
+ */
518
+ this.config = injectContextMenuConfig();
519
+ /**
520
+ * The menu template ref or ComponentType to display.
521
+ */
522
+ this.menu = input(undefined, ...(ngDevMode ? [{ debugName: "menu", alias: 'ngpContextMenuTrigger' }] : [{
523
+ alias: 'ngpContextMenuTrigger',
524
+ }]));
525
+ /**
526
+ * Define if the trigger should be disabled.
527
+ * @default false
528
+ */
529
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpContextMenuTriggerDisabled',
530
+ transform: booleanAttribute }] : [{
531
+ alias: 'ngpContextMenuTriggerDisabled',
532
+ transform: booleanAttribute,
533
+ }]));
534
+ /**
535
+ * Define the offset of the context menu relative to the cursor position.
536
+ * @default 2
537
+ */
538
+ this.offset = input(this.config.offset, ...(ngDevMode ? [{ debugName: "offset", alias: 'ngpContextMenuTriggerOffset',
539
+ transform: coerceOffset }] : [{
540
+ alias: 'ngpContextMenuTriggerOffset',
541
+ transform: coerceOffset,
542
+ }]));
543
+ /**
544
+ * Define whether the context menu should flip when there is not enough space.
545
+ * @default true
546
+ */
547
+ this.flip = input(this.config.flip, ...(ngDevMode ? [{ debugName: "flip", alias: 'ngpContextMenuTriggerFlip',
548
+ transform: coerceFlip }] : [{
549
+ alias: 'ngpContextMenuTriggerFlip',
550
+ transform: coerceFlip,
551
+ }]));
552
+ /**
553
+ * Configure shift behavior to keep the context menu in view.
554
+ * @default undefined
555
+ */
556
+ this.shift = input(this.config.shift, ...(ngDevMode ? [{ debugName: "shift", alias: 'ngpContextMenuTriggerShift',
557
+ transform: coerceShift }] : [{
558
+ alias: 'ngpContextMenuTriggerShift',
559
+ transform: coerceShift,
560
+ }]));
561
+ /**
562
+ * Define the container in which the context menu should be attached.
563
+ * @default document.body
564
+ */
565
+ this.container = input(this.config.container, ...(ngDevMode ? [{ debugName: "container", alias: 'ngpContextMenuTriggerContainer' }] : [{
566
+ alias: 'ngpContextMenuTriggerContainer',
567
+ }]));
568
+ /**
569
+ * Defines how the context menu behaves when the window is scrolled.
570
+ * @default 'close'
571
+ */
572
+ this.scrollBehavior = input(this.config.scrollBehavior, ...(ngDevMode ? [{ debugName: "scrollBehavior", alias: 'ngpContextMenuTriggerScrollBehavior' }] : [{
573
+ alias: 'ngpContextMenuTriggerScrollBehavior',
574
+ }]));
575
+ /**
576
+ * Provide context to the menu. This can be used to pass data to the menu content.
577
+ */
578
+ this.context = input(undefined, ...(ngDevMode ? [{ debugName: "context", alias: 'ngpContextMenuTriggerContext' }] : [{
579
+ alias: 'ngpContextMenuTriggerContext',
580
+ }]));
581
+ /**
582
+ * The context menu trigger state.
583
+ */
584
+ this.state = ngpContextMenuTrigger({
585
+ disabled: this.disabled,
586
+ menu: this.menu,
587
+ offset: this.offset,
588
+ flip: this.flip,
589
+ shift: this.shift(),
590
+ container: this.container,
591
+ scrollBehavior: this.scrollBehavior,
592
+ context: this.context,
593
+ });
594
+ }
595
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
596
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpContextMenuTrigger, isStandalone: true, selector: "[ngpContextMenuTrigger]", inputs: { menu: { classPropertyName: "menu", publicName: "ngpContextMenuTrigger", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpContextMenuTriggerDisabled", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "ngpContextMenuTriggerOffset", isSignal: true, isRequired: false, transformFunction: null }, flip: { classPropertyName: "flip", publicName: "ngpContextMenuTriggerFlip", isSignal: true, isRequired: false, transformFunction: null }, shift: { classPropertyName: "shift", publicName: "ngpContextMenuTriggerShift", isSignal: true, isRequired: false, transformFunction: null }, container: { classPropertyName: "container", publicName: "ngpContextMenuTriggerContainer", isSignal: true, isRequired: false, transformFunction: null }, scrollBehavior: { classPropertyName: "scrollBehavior", publicName: "ngpContextMenuTriggerScrollBehavior", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "ngpContextMenuTriggerContext", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideContextMenuTriggerState(), provideMenuTriggerState()], exportAs: ["ngpContextMenuTrigger"], ngImport: i0 }); }
597
+ }
598
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpContextMenuTrigger, decorators: [{
599
+ type: Directive,
600
+ args: [{
601
+ selector: '[ngpContextMenuTrigger]',
602
+ exportAs: 'ngpContextMenuTrigger',
603
+ providers: [provideContextMenuTriggerState(), provideMenuTriggerState()],
604
+ }]
605
+ }], propDecorators: { menu: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuTrigger", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuTriggerDisabled", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuTriggerOffset", required: false }] }], flip: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuTriggerFlip", required: false }] }], shift: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuTriggerShift", required: false }] }], container: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuTriggerContainer", required: false }] }], scrollBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuTriggerScrollBehavior", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpContextMenuTriggerContext", required: false }] }] } });
606
+
607
+ /**
608
+ * Generated bundle index. Do not edit.
609
+ */
610
+
611
+ export { NgpContextMenu, NgpContextMenuItem, NgpContextMenuItemCheckbox, NgpContextMenuItemIndicator, NgpContextMenuItemRadio, NgpContextMenuItemRadioGroup, NgpContextMenuSubmenuTrigger, NgpContextMenuTrigger, injectContextMenuTriggerState, ngpContextMenuTrigger, provideContextMenuConfig, provideContextMenuTriggerState };
612
+ //# sourceMappingURL=ng-primitives-context-menu.mjs.map