ng-primitives 0.44.0 → 0.45.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.
Files changed (45) hide show
  1. package/combobox/combobox/combobox-state.d.ts +3 -2
  2. package/combobox/combobox/combobox.d.ts +9 -3
  3. package/combobox/combobox-button/combobox-button.d.ts +3 -2
  4. package/combobox/combobox-dropdown/combobox-dropdown.d.ts +52 -2
  5. package/combobox/combobox-input/combobox-input.d.ts +3 -2
  6. package/combobox/combobox-option/combobox-option.d.ts +3 -2
  7. package/combobox/combobox-portal/combobox-portal.d.ts +8 -21
  8. package/example-theme/index.css +1 -0
  9. package/fesm2022/ng-primitives-combobox.mjs +63 -87
  10. package/fesm2022/ng-primitives-combobox.mjs.map +1 -1
  11. package/fesm2022/ng-primitives-focus-trap.mjs +9 -1
  12. package/fesm2022/ng-primitives-focus-trap.mjs.map +1 -1
  13. package/fesm2022/ng-primitives-listbox.mjs +1 -1
  14. package/fesm2022/ng-primitives-listbox.mjs.map +1 -1
  15. package/fesm2022/ng-primitives-menu.mjs +372 -63
  16. package/fesm2022/ng-primitives-menu.mjs.map +1 -1
  17. package/fesm2022/ng-primitives-popover.mjs +62 -332
  18. package/fesm2022/ng-primitives-popover.mjs.map +1 -1
  19. package/fesm2022/ng-primitives-portal.mjs +359 -2
  20. package/fesm2022/ng-primitives-portal.mjs.map +1 -1
  21. package/fesm2022/ng-primitives-tooltip.mjs +51 -176
  22. package/fesm2022/ng-primitives-tooltip.mjs.map +1 -1
  23. package/focus-trap/focus-trap/focus-trap-state.d.ts +1 -0
  24. package/focus-trap/focus-trap/focus-trap.d.ts +5 -0
  25. package/menu/config/menu-config.d.ts +42 -0
  26. package/menu/index.d.ts +5 -1
  27. package/menu/menu/menu.d.ts +6 -2
  28. package/menu/menu-trigger/menu-trigger-state.d.ts +37 -0
  29. package/menu/menu-trigger/menu-trigger.d.ts +87 -13
  30. package/menu/submenu-trigger/submenu-trigger-state.d.ts +56 -0
  31. package/menu/submenu-trigger/submenu-trigger.d.ts +62 -10
  32. package/package.json +1 -1
  33. package/popover/index.d.ts +1 -2
  34. package/popover/popover/popover.d.ts +2 -43
  35. package/popover/popover-trigger/popover-trigger.d.ts +13 -107
  36. package/portal/index.d.ts +2 -0
  37. package/portal/overlay-token.d.ts +12 -0
  38. package/portal/overlay.d.ts +170 -0
  39. package/schematics/ng-generate/templates/combobox/combobox.__fileSuffix@dasherize__.ts.template +31 -0
  40. package/tooltip/index.d.ts +1 -1
  41. package/tooltip/tooltip/tooltip.d.ts +3 -25
  42. package/tooltip/tooltip-trigger/tooltip-trigger.d.ts +12 -63
  43. package/popover/popover/popover-token.d.ts +0 -10
  44. package/popover/utils/transform-origin.d.ts +0 -2
  45. package/tooltip/tooltip/tooltip-token.d.ts +0 -10
@@ -19,12 +19,13 @@ export declare const injectComboboxState: <U = {
19
19
  readonly disabled: import("@angular/core").InputSignalWithTransform<boolean, import("@angular/cdk/coercion").BooleanInput>;
20
20
  readonly openChange: import("@angular/core").OutputEmitterRef<boolean>;
21
21
  readonly compareWith: import("@angular/core").InputSignal<(a: any | undefined, b: any | undefined) => boolean>;
22
- readonly dropdownPosition: import("@angular/core").InputSignal<"top" | "bottom" | "auto">;
22
+ readonly placement: import("@angular/core").InputSignal<import("@floating-ui/dom").Placement>;
23
23
  readonly input: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxInput | undefined>;
24
24
  readonly button: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxButton | undefined>;
25
25
  readonly portal: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxPortal | undefined>;
26
26
  readonly dropdown: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxDropdown | undefined>;
27
27
  readonly options: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxOption[]>;
28
+ readonly overlay: import("@angular/core").Signal<import("ng-primitives/portal").NgpOverlay<void> | null | undefined>;
28
29
  readonly open: import("@angular/core").Signal<boolean>;
29
30
  readonly activeDescendantManager: {
30
31
  activeDescendant: import("@angular/core").Signal<string | undefined>;
@@ -37,7 +38,7 @@ export declare const injectComboboxState: <U = {
37
38
  reset: () => void;
38
39
  };
39
40
  readonly state: import("ng-primitives/state").CreatedState<NgpCombobox>;
40
- openDropdown: () => void;
41
+ openDropdown: () => Promise<void>;
41
42
  closeDropdown: () => void;
42
43
  toggleDropdown: () => void;
43
44
  selectOption: (option: import("ng-primitives/combobox").NgpComboboxOption) => void;
@@ -1,5 +1,6 @@
1
1
  import { BooleanInput } from '@angular/cdk/coercion';
2
2
  import { Injector } from '@angular/core';
3
+ import { Placement } from '@floating-ui/dom';
3
4
  import type { NgpComboboxButton } from '../combobox-button/combobox-button';
4
5
  import type { NgpComboboxDropdown } from '../combobox-dropdown/combobox-dropdown';
5
6
  import type { NgpComboboxInput } from '../combobox-input/combobox-input';
@@ -36,7 +37,7 @@ export declare class NgpCombobox {
36
37
  /** The comparator function used to compare options. */
37
38
  readonly compareWith: import("@angular/core").InputSignal<(a: T | undefined, b: T | undefined) => boolean>;
38
39
  /** The position of the dropdown. */
39
- readonly dropdownPosition: import("@angular/core").InputSignal<"top" | "bottom" | "auto">;
40
+ readonly placement: import("@angular/core").InputSignal<Placement>;
40
41
  /**
41
42
  * Store the combobox input
42
43
  * @internal
@@ -62,6 +63,11 @@ export declare class NgpCombobox {
62
63
  * @internal
63
64
  */
64
65
  readonly options: import("@angular/core").WritableSignal<NgpComboboxOption[]>;
66
+ /**
67
+ * Access the overlay
68
+ * @internal
69
+ */
70
+ readonly overlay: import("@angular/core").Signal<import("ng-primitives/portal").NgpOverlay<void> | null | undefined>;
65
71
  /**
66
72
  * The open state of the combobox.
67
73
  * @internal
@@ -88,7 +94,7 @@ export declare class NgpCombobox {
88
94
  * Open the dropdown.
89
95
  * @internal
90
96
  */
91
- openDropdown(): void;
97
+ openDropdown(): Promise<void>;
92
98
  /**
93
99
  * Close the dropdown.
94
100
  * @internal
@@ -171,6 +177,6 @@ export declare class NgpCombobox {
171
177
  */
172
178
  unregisterOption(option: NgpComboboxOption): void;
173
179
  static ɵfac: i0.ɵɵFactoryDeclaration<NgpCombobox, never>;
174
- static ɵdir: i0.ɵɵDirectiveDeclaration<NgpCombobox, "[ngpCombobox]", ["ngpCombobox"], { "value": { "alias": "ngpComboboxValue"; "required": false; "isSignal": true; }; "multiple": { "alias": "ngpComboboxMultiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "ngpComboboxDisabled"; "required": false; "isSignal": true; }; "compareWith": { "alias": "ngpComboboxCompareWith"; "required": false; "isSignal": true; }; "dropdownPosition": { "alias": "ngpComboboxDropdownPosition"; "required": false; "isSignal": true; }; }, { "valueChange": "ngpComboboxValueChange"; "openChange": "ngpComboboxOpenChange"; }, never, never, true, never>;
180
+ static ɵdir: i0.ɵɵDirectiveDeclaration<NgpCombobox, "[ngpCombobox]", ["ngpCombobox"], { "value": { "alias": "ngpComboboxValue"; "required": false; "isSignal": true; }; "multiple": { "alias": "ngpComboboxMultiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "ngpComboboxDisabled"; "required": false; "isSignal": true; }; "compareWith": { "alias": "ngpComboboxCompareWith"; "required": false; "isSignal": true; }; "placement": { "alias": "ngpComboboxDropdownPlacement"; "required": false; "isSignal": true; }; }, { "valueChange": "ngpComboboxValueChange"; "openChange": "ngpComboboxOpenChange"; }, never, never, true, never>;
175
181
  }
176
182
  export {};
@@ -10,12 +10,13 @@ export declare class NgpComboboxButton {
10
10
  readonly disabled: import("@angular/core").InputSignalWithTransform<boolean, import("@angular/cdk/coercion").BooleanInput>;
11
11
  readonly openChange: import("@angular/core").OutputEmitterRef<boolean>;
12
12
  readonly compareWith: import("@angular/core").InputSignal<(a: any | undefined, b: any | undefined) => boolean>;
13
- readonly dropdownPosition: import("@angular/core").InputSignal<"top" | "bottom" | "auto">;
13
+ readonly placement: import("@angular/core").InputSignal<import("@floating-ui/dom").Placement>;
14
14
  readonly input: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxInput | undefined>;
15
15
  readonly button: import("@angular/core").WritableSignal<NgpComboboxButton | undefined>;
16
16
  readonly portal: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxPortal | undefined>;
17
17
  readonly dropdown: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxDropdown | undefined>;
18
18
  readonly options: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxOption[]>;
19
+ readonly overlay: import("@angular/core").Signal<import("ng-primitives/portal").NgpOverlay<void> | null | undefined>;
19
20
  readonly open: import("@angular/core").Signal<boolean>;
20
21
  readonly activeDescendantManager: {
21
22
  activeDescendant: import("@angular/core").Signal<string | undefined>;
@@ -28,7 +29,7 @@ export declare class NgpComboboxButton {
28
29
  reset: () => void;
29
30
  };
30
31
  readonly state: import("ng-primitives/state").CreatedState<import("ng-primitives/combobox").NgpCombobox>;
31
- openDropdown: () => void;
32
+ openDropdown: () => Promise<void>;
32
33
  closeDropdown: () => void;
33
34
  toggleDropdown: () => void;
34
35
  selectOption: (option: import("ng-primitives/combobox").NgpComboboxOption) => void;
@@ -1,7 +1,57 @@
1
1
  import * as i0 from "@angular/core";
2
+ import * as i1 from "ng-primitives/internal";
2
3
  export declare class NgpComboboxDropdown {
3
4
  /** Access the combobox state. */
4
- private readonly state;
5
+ protected readonly state: import("@angular/core").Signal<import("ng-primitives/state").State<{
6
+ readonly elementRef: import("@angular/core").ElementRef<HTMLElement>;
7
+ readonly injector: import("@angular/core").Injector;
8
+ readonly value: import("@angular/core").InputSignal<any>;
9
+ readonly valueChange: import("@angular/core").OutputEmitterRef<any>;
10
+ readonly multiple: import("@angular/core").InputSignalWithTransform<boolean, import("@angular/cdk/coercion").BooleanInput>;
11
+ readonly disabled: import("@angular/core").InputSignalWithTransform<boolean, import("@angular/cdk/coercion").BooleanInput>;
12
+ readonly openChange: import("@angular/core").OutputEmitterRef<boolean>;
13
+ readonly compareWith: import("@angular/core").InputSignal<(a: any | undefined, b: any | undefined) => boolean>;
14
+ readonly placement: import("@angular/core").InputSignal<import("@floating-ui/dom").Placement>;
15
+ readonly input: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxInput | undefined>;
16
+ readonly button: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxButton | undefined>;
17
+ readonly portal: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxPortal | undefined>;
18
+ readonly dropdown: import("@angular/core").WritableSignal<NgpComboboxDropdown | undefined>;
19
+ readonly options: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxOption[]>;
20
+ readonly overlay: import("@angular/core").Signal<import("ng-primitives/portal").NgpOverlay<void> | null | undefined>;
21
+ readonly open: import("@angular/core").Signal<boolean>;
22
+ readonly activeDescendantManager: {
23
+ activeDescendant: import("@angular/core").Signal<string | undefined>;
24
+ activeItem: import("@angular/core").Signal<import("ng-primitives/combobox").NgpComboboxOption | undefined>;
25
+ activate: (item: import("ng-primitives/combobox").NgpComboboxOption | undefined) => void;
26
+ first: () => void;
27
+ last: () => void;
28
+ next: () => void;
29
+ previous: () => void;
30
+ reset: () => void;
31
+ };
32
+ readonly state: import("ng-primitives/state").CreatedState<import("ng-primitives/combobox").NgpCombobox>;
33
+ openDropdown: () => Promise<void>;
34
+ closeDropdown: () => void;
35
+ toggleDropdown: () => void;
36
+ selectOption: (option: import("ng-primitives/combobox").NgpComboboxOption) => void;
37
+ deselectOption: (option: import("ng-primitives/combobox").NgpComboboxOption) => void;
38
+ toggleOption: (option: import("ng-primitives/combobox").NgpComboboxOption) => void;
39
+ isOptionSelected: (option: import("ng-primitives/combobox").NgpComboboxOption) => boolean;
40
+ activateNextOption: () => void;
41
+ activatePreviousOption: () => void;
42
+ registerPortal: (portal: import("ng-primitives/combobox").NgpComboboxPortal) => void;
43
+ registerInput: (input: import("ng-primitives/combobox").NgpComboboxInput) => void;
44
+ registerButton: (button: import("ng-primitives/combobox").NgpComboboxButton) => void;
45
+ registerDropdown: (dropdown: NgpComboboxDropdown) => void;
46
+ registerOption: (option: import("ng-primitives/combobox").NgpComboboxOption) => void;
47
+ unregisterOption: (option: import("ng-primitives/combobox").NgpComboboxOption) => void;
48
+ }>>;
49
+ /** The dimensions of the combobox. */
50
+ protected readonly comboboxDimensions: import("@angular/core").Signal<import("ng-primitives/resize").Dimensions>;
51
+ /** The dimensions of the combobox. */
52
+ protected readonly inputDimensions: import("@angular/core").Signal<import("ng-primitives/resize").Dimensions>;
53
+ /** Store the combobox button dimensions. */
54
+ protected readonly buttonDimensions: import("@angular/core").Signal<import("ng-primitives/resize").Dimensions>;
5
55
  /**
6
56
  * Access the element reference.
7
57
  * @internal
@@ -11,5 +61,5 @@ export declare class NgpComboboxDropdown {
11
61
  readonly id: import("@angular/core").InputSignal<string>;
12
62
  constructor();
13
63
  static ɵfac: i0.ɵɵFactoryDeclaration<NgpComboboxDropdown, never>;
14
- static ɵdir: i0.ɵɵDirectiveDeclaration<NgpComboboxDropdown, "[ngpComboboxDropdown]", ["ngpComboboxDropdown"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
64
+ static ɵdir: i0.ɵɵDirectiveDeclaration<NgpComboboxDropdown, "[ngpComboboxDropdown]", ["ngpComboboxDropdown"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1.NgpExitAnimation; inputs: {}; outputs: {}; }]>;
15
65
  }
@@ -11,12 +11,13 @@ export declare class NgpComboboxInput {
11
11
  readonly disabled: import("@angular/core").InputSignalWithTransform<boolean, import("@angular/cdk/coercion").BooleanInput>;
12
12
  readonly openChange: import("@angular/core").OutputEmitterRef<boolean>;
13
13
  readonly compareWith: import("@angular/core").InputSignal<(a: any | undefined, b: any | undefined) => boolean>;
14
- readonly dropdownPosition: import("@angular/core").InputSignal<"top" | "bottom" | "auto">;
14
+ readonly placement: import("@angular/core").InputSignal<import("@floating-ui/dom").Placement>;
15
15
  readonly input: import("@angular/core").WritableSignal<NgpComboboxInput | undefined>;
16
16
  readonly button: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxButton | undefined>;
17
17
  readonly portal: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxPortal | undefined>;
18
18
  readonly dropdown: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxDropdown | undefined>;
19
19
  readonly options: import("@angular/core").WritableSignal<NgpComboboxOption[]>;
20
+ readonly overlay: import("@angular/core").Signal<import("ng-primitives/portal").NgpOverlay<void> | null | undefined>;
20
21
  readonly open: import("@angular/core").Signal<boolean>;
21
22
  readonly activeDescendantManager: {
22
23
  activeDescendant: import("@angular/core").Signal<string | undefined>;
@@ -29,7 +30,7 @@ export declare class NgpComboboxInput {
29
30
  reset: () => void;
30
31
  };
31
32
  readonly state: import("ng-primitives/state").CreatedState<import("ng-primitives/combobox").NgpCombobox>;
32
- openDropdown: () => void;
33
+ openDropdown: () => Promise<void>;
33
34
  closeDropdown: () => void;
34
35
  toggleDropdown: () => void;
35
36
  selectOption: (option: NgpComboboxOption) => void;
@@ -13,12 +13,13 @@ export declare class NgpComboboxOption implements OnInit, OnDestroy, NgpActivata
13
13
  readonly disabled: import("@angular/core").InputSignalWithTransform<boolean, BooleanInput>;
14
14
  readonly openChange: import("@angular/core").OutputEmitterRef<boolean>;
15
15
  readonly compareWith: import("@angular/core").InputSignal<(a: any | undefined, b: any | undefined) => boolean>;
16
- readonly dropdownPosition: import("@angular/core").InputSignal<"top" | "bottom" | "auto">;
16
+ readonly placement: import("@angular/core").InputSignal<import("@floating-ui/dom").Placement>;
17
17
  readonly input: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxInput | undefined>;
18
18
  readonly button: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxButton | undefined>;
19
19
  readonly portal: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxPortal | undefined>;
20
20
  readonly dropdown: import("@angular/core").WritableSignal<import("ng-primitives/combobox").NgpComboboxDropdown | undefined>;
21
21
  readonly options: import("@angular/core").WritableSignal<NgpComboboxOption[]>;
22
+ readonly overlay: import("@angular/core").Signal<import("ng-primitives/portal").NgpOverlay<void> | null | undefined>;
22
23
  readonly open: import("@angular/core").Signal<boolean>;
23
24
  readonly activeDescendantManager: {
24
25
  activeDescendant: import("@angular/core").Signal<string | undefined>;
@@ -31,7 +32,7 @@ export declare class NgpComboboxOption implements OnInit, OnDestroy, NgpActivata
31
32
  reset: () => void;
32
33
  };
33
34
  readonly state: import("ng-primitives/state").CreatedState<import("ng-primitives/combobox").NgpCombobox>;
34
- openDropdown: () => void;
35
+ openDropdown: () => Promise<void>;
35
36
  closeDropdown: () => void;
36
37
  toggleDropdown: () => void;
37
38
  selectOption: (option: NgpComboboxOption) => void;
@@ -1,35 +1,18 @@
1
1
  import { OnDestroy } from '@angular/core';
2
- import { NgpPortal } from 'ng-primitives/portal';
2
+ import { NgpOverlay } from 'ng-primitives/portal';
3
3
  import * as i0 from "@angular/core";
4
4
  export declare class NgpComboboxPortal implements OnDestroy {
5
5
  /** Access the combobox state. */
6
6
  private readonly state;
7
7
  /** Access the template reference. */
8
8
  private readonly templateRef;
9
- /** Access the view container reference. */
10
- private readonly viewContainerRef;
11
9
  /** Access the injector. */
12
10
  private readonly injector;
13
- /** Access the document. */
14
- private readonly document;
15
11
  /**
16
- * Store the embedded view reference.
12
+ * The overlay that manages the popover
17
13
  * @internal
18
14
  */
19
- readonly viewRef: import("@angular/core").WritableSignal<NgpPortal | null>;
20
- /** Store the dispose function. */
21
- private dispose;
22
- /** The position of the dropdown. */
23
- readonly position: import("@angular/core").WritableSignal<{
24
- x: number;
25
- y: number;
26
- }>;
27
- /** The dimensions of the combobox. */
28
- private readonly comboboxDimensions;
29
- /** The dimensions of the combobox. */
30
- private readonly inputDimensions;
31
- /** Store the combobox button dimensions. */
32
- private readonly buttonDimensions;
15
+ readonly overlay: import("@angular/core").WritableSignal<NgpOverlay<void> | null>;
33
16
  constructor();
34
17
  /** Cleanup the portal. */
35
18
  ngOnDestroy(): void;
@@ -37,12 +20,16 @@ export declare class NgpComboboxPortal implements OnDestroy {
37
20
  * Attach the portal.
38
21
  * @internal
39
22
  */
40
- attach(): void;
23
+ show(): Promise<void>;
41
24
  /**
42
25
  * Detach the portal.
43
26
  * @internal
44
27
  */
45
28
  detach(): Promise<void>;
29
+ /**
30
+ * Create the overlay that will contain the dropdown
31
+ */
32
+ private createOverlay;
46
33
  static ɵfac: i0.ɵɵFactoryDeclaration<NgpComboboxPortal, never>;
47
34
  static ɵdir: i0.ɵɵDirectiveDeclaration<NgpComboboxPortal, "[ngpComboboxPortal]", ["ngpComboboxPortal"], {}, {}, never, never, true, never>;
48
35
  }
@@ -42,6 +42,7 @@
42
42
  --ngp-text-red: #ef4444;
43
43
 
44
44
  --ngp-shadow: 0 1px 2px 0 rgba(0 0 0 0.05);
45
+ --ngp-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
45
46
  --ngp-shadow-border: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
46
47
 
47
48
  --ngp-focus-ring: rgb(59 130 246);
@@ -1,12 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, Directive, computed, HostListener, booleanAttribute, inject, TemplateRef, ViewContainerRef, Injector, signal, effect, output, afterNextRender } from '@angular/core';
3
- import { injectElementRef, setupInteractions, explicitEffect } from 'ng-primitives/internal';
2
+ import { input, Directive, computed, HostListener, booleanAttribute, inject, TemplateRef, Injector, signal, output, afterNextRender } from '@angular/core';
3
+ import * as i1 from 'ng-primitives/internal';
4
+ import { injectElementRef, NgpExitAnimation, setupInteractions, explicitEffect, provideExitAnimationManager } from 'ng-primitives/internal';
5
+ import { observeResize } from 'ng-primitives/resize';
4
6
  import { uniqueId } from 'ng-primitives/utils';
5
7
  import { createStateToken, createStateProvider, createStateInjector, createState } from 'ng-primitives/state';
6
- import { DOCUMENT } from '@angular/common';
7
- import { flip, autoUpdate, computePosition } from '@floating-ui/dom';
8
- import { createPortal } from 'ng-primitives/portal';
9
- import { observeResize } from 'ng-primitives/resize';
8
+ import { createOverlay } from 'ng-primitives/portal';
10
9
  import { activeDescendantManager } from 'ng-primitives/a11y';
11
10
 
12
11
  /**
@@ -30,6 +29,12 @@ class NgpComboboxDropdown {
30
29
  constructor() {
31
30
  /** Access the combobox state. */
32
31
  this.state = injectComboboxState();
32
+ /** The dimensions of the combobox. */
33
+ this.comboboxDimensions = observeResize(() => this.state().elementRef.nativeElement);
34
+ /** The dimensions of the combobox. */
35
+ this.inputDimensions = observeResize(() => this.state().input()?.elementRef.nativeElement);
36
+ /** Store the combobox button dimensions. */
37
+ this.buttonDimensions = observeResize(() => this.state().button()?.elementRef.nativeElement);
33
38
  /**
34
39
  * Access the element reference.
35
40
  * @internal
@@ -40,16 +45,23 @@ class NgpComboboxDropdown {
40
45
  this.state().registerDropdown(this);
41
46
  }
42
47
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpComboboxDropdown, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
43
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgpComboboxDropdown, isStandalone: true, selector: "[ngpComboboxDropdown]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "listbox" }, properties: { "id": "id()" } }, exportAs: ["ngpComboboxDropdown"], ngImport: i0 }); }
48
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgpComboboxDropdown, isStandalone: true, selector: "[ngpComboboxDropdown]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "listbox" }, properties: { "id": "id()", "style.left.px": "state().overlay()?.position()?.x", "style.top.px": "state().overlay()?.position()?.y", "style.--ngp-combobox-transform-origin": "state().overlay()?.transformOrigin()", "style.--ngp-combobox-width.px": "comboboxDimensions().width", "style.--ngp-combobox-input-width.px": "inputDimensions().width", "style.--ngp-combobox-button-width.px": "buttonDimensions().width" } }, exportAs: ["ngpComboboxDropdown"], hostDirectives: [{ directive: i1.NgpExitAnimation }], ngImport: i0 }); }
44
49
  }
45
50
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpComboboxDropdown, decorators: [{
46
51
  type: Directive,
47
52
  args: [{
48
53
  selector: '[ngpComboboxDropdown]',
49
54
  exportAs: 'ngpComboboxDropdown',
55
+ hostDirectives: [NgpExitAnimation],
50
56
  host: {
51
57
  role: 'listbox',
52
58
  '[id]': 'id()',
59
+ '[style.left.px]': 'state().overlay()?.position()?.x',
60
+ '[style.top.px]': 'state().overlay()?.position()?.y',
61
+ '[style.--ngp-combobox-transform-origin]': 'state().overlay()?.transformOrigin()',
62
+ '[style.--ngp-combobox-width.px]': 'comboboxDimensions().width',
63
+ '[style.--ngp-combobox-input-width.px]': 'inputDimensions().width',
64
+ '[style.--ngp-combobox-button-width.px]': 'buttonDimensions().width',
53
65
  },
54
66
  }]
55
67
  }], ctorParameters: () => [] });
@@ -329,99 +341,54 @@ class NgpComboboxPortal {
329
341
  this.state = injectComboboxState();
330
342
  /** Access the template reference. */
331
343
  this.templateRef = inject(TemplateRef);
332
- /** Access the view container reference. */
333
- this.viewContainerRef = inject(ViewContainerRef);
334
344
  /** Access the injector. */
335
345
  this.injector = inject(Injector);
336
- /** Access the document. */
337
- this.document = inject(DOCUMENT);
338
346
  /**
339
- * Store the embedded view reference.
347
+ * The overlay that manages the popover
340
348
  * @internal
341
349
  */
342
- this.viewRef = signal(null);
343
- /** Store the dispose function. */
344
- this.dispose = null;
345
- /** The position of the dropdown. */
346
- this.position = signal({ x: 0, y: 0 });
347
- /** The dimensions of the combobox. */
348
- this.comboboxDimensions = observeResize(() => this.state().elementRef.nativeElement);
349
- /** The dimensions of the combobox. */
350
- this.inputDimensions = observeResize(() => this.state().input()?.elementRef.nativeElement);
351
- /** Store the combobox button dimensions. */
352
- this.buttonDimensions = observeResize(() => this.state().button()?.elementRef.nativeElement);
350
+ this.overlay = signal(null);
353
351
  this.state().registerPortal(this);
354
- effect(() => {
355
- const dropdownElement = this.viewRef()?.getElements()[0];
356
- if (!dropdownElement) {
357
- return;
358
- }
359
- const position = this.position();
360
- const comboboxWidth = this.comboboxDimensions().width;
361
- const inputWidth = this.inputDimensions().width;
362
- const buttonWidth = this.buttonDimensions().width;
363
- if (!dropdownElement) {
364
- return;
365
- }
366
- const styles = {
367
- position: 'absolute',
368
- left: `${position.x}px`,
369
- top: `${position.y}px`,
370
- '--ngp-combobox-width': `${comboboxWidth}px`,
371
- '--ngp-combobox-input-width': `${inputWidth}px`,
372
- '--ngp-combobox-button-width': `${buttonWidth}px`,
373
- };
374
- for (const [key, value] of Object.entries(styles)) {
375
- dropdownElement.style.setProperty(key, value);
376
- }
377
- });
378
352
  }
379
353
  /** Cleanup the portal. */
380
354
  ngOnDestroy() {
381
- this.detach();
382
- this.dispose?.();
355
+ this.overlay()?.destroy();
383
356
  }
384
357
  /**
385
358
  * Attach the portal.
386
359
  * @internal
387
360
  */
388
- attach() {
389
- const viewRef = createPortal(this.templateRef, this.viewContainerRef, this.injector);
390
- viewRef.attach(this.document.body);
391
- viewRef.detectChanges();
392
- this.viewRef.set(viewRef);
393
- const dropdownElement = this.viewRef()?.getElements()[0];
394
- if (!dropdownElement) {
395
- throw new Error('Dropdown element not found');
361
+ show() {
362
+ // Create the overlay if it doesn't exist yet
363
+ if (!this.overlay()) {
364
+ this.createOverlay();
396
365
  }
397
- let placement;
398
- const middleware = [];
399
- switch (this.state().dropdownPosition()) {
400
- case 'top':
401
- placement = 'top-start';
402
- break;
403
- case 'bottom':
404
- placement = 'bottom-start';
405
- break;
406
- case 'auto':
407
- placement = 'bottom-start';
408
- middleware.push(flip({ fallbackPlacements: ['top-start'] }));
409
- break;
410
- }
411
- this.dispose = autoUpdate(this.state().elementRef.nativeElement, dropdownElement, async () => {
412
- const position = await computePosition(this.state().elementRef.nativeElement, dropdownElement, { placement, middleware, strategy: 'absolute' });
413
- this.position.set({ x: position.x, y: position.y });
414
- });
366
+ // Show the overlay
367
+ return this.overlay().show();
415
368
  }
416
369
  /**
417
370
  * Detach the portal.
418
371
  * @internal
419
372
  */
420
373
  async detach() {
421
- await this.viewRef()?.detach();
422
- this.viewRef.set(null);
423
- this.dispose?.();
424
- this.dispose = null;
374
+ this.overlay()?.hide();
375
+ }
376
+ /**
377
+ * Create the overlay that will contain the dropdown
378
+ */
379
+ createOverlay() {
380
+ // Create config for the overlay
381
+ const config = {
382
+ content: this.templateRef,
383
+ triggerElement: this.state().elementRef.nativeElement,
384
+ injector: this.injector,
385
+ placement: this.state().placement(),
386
+ closeOnOutsideClick: true,
387
+ closeOnEscape: true,
388
+ restoreFocus: false,
389
+ scrollBehaviour: 'reposition',
390
+ };
391
+ this.overlay.set(createOverlay(config));
425
392
  }
426
393
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpComboboxPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
427
394
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.11", type: NgpComboboxPortal, isStandalone: true, selector: "[ngpComboboxPortal]", exportAs: ["ngpComboboxPortal"], ngImport: i0 }); }
@@ -467,8 +434,8 @@ class NgpCombobox {
467
434
  alias: 'ngpComboboxCompareWith',
468
435
  });
469
436
  /** The position of the dropdown. */
470
- this.dropdownPosition = input('bottom', {
471
- alias: 'ngpComboboxDropdownPosition',
437
+ this.placement = input('bottom', {
438
+ alias: 'ngpComboboxDropdownPlacement',
472
439
  });
473
440
  /**
474
441
  * Store the combobox input
@@ -495,11 +462,16 @@ class NgpCombobox {
495
462
  * @internal
496
463
  */
497
464
  this.options = signal([]);
465
+ /**
466
+ * Access the overlay
467
+ * @internal
468
+ */
469
+ this.overlay = computed(() => this.portal()?.overlay());
498
470
  /**
499
471
  * The open state of the combobox.
500
472
  * @internal
501
473
  */
502
- this.open = computed(() => this.portal()?.viewRef() !== null);
474
+ this.open = computed(() => this.overlay()?.isOpen() ?? false);
503
475
  /**
504
476
  * The active key descendant manager.
505
477
  * @internal
@@ -521,11 +493,11 @@ class NgpCombobox {
521
493
  * Open the dropdown.
522
494
  * @internal
523
495
  */
524
- openDropdown() {
496
+ async openDropdown() {
525
497
  if (this.state.disabled() || this.open()) {
526
498
  return;
527
499
  }
528
- this.portal()?.attach();
500
+ await this.portal()?.show();
529
501
  // if there is a selected option(s), set the active descendant to the first selected option
530
502
  const selectedOption = this.options().find(option => this.isOptionSelected(option));
531
503
  // if there is no selected option, set the active descendant to the first option
@@ -568,10 +540,14 @@ class NgpCombobox {
568
540
  * @internal
569
541
  */
570
542
  selectOption(option) {
571
- if (this.state.disabled() || this.isOptionSelected(option)) {
543
+ if (this.state.disabled()) {
572
544
  return;
573
545
  }
574
546
  if (this.state.multiple()) {
547
+ // if the option is already selected, do nothing
548
+ if (this.isOptionSelected(option)) {
549
+ return;
550
+ }
575
551
  const value = [...this.state.value(), option.value()];
576
552
  // add the option to the value
577
553
  this.state.value.set(value);
@@ -733,14 +709,14 @@ class NgpCombobox {
733
709
  this.options.update(options => options.filter(o => o !== option));
734
710
  }
735
711
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpCombobox, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
736
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgpCombobox, isStandalone: true, selector: "[ngpCombobox]", inputs: { value: { classPropertyName: "value", publicName: "ngpComboboxValue", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "ngpComboboxMultiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpComboboxDisabled", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "ngpComboboxCompareWith", isSignal: true, isRequired: false, transformFunction: null }, dropdownPosition: { classPropertyName: "dropdownPosition", publicName: "ngpComboboxDropdownPosition", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "ngpComboboxValueChange", openChange: "ngpComboboxOpenChange" }, host: { properties: { "attr.data-open": "state.open() ? \"\" : undefined", "attr.data-disabled": "state.disabled() ? \"\" : undefined", "attr.data-multiple": "state.multiple() ? \"\" : undefined" } }, providers: [provideComboboxState()], exportAs: ["ngpCombobox"], ngImport: i0 }); }
712
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgpCombobox, isStandalone: true, selector: "[ngpCombobox]", inputs: { value: { classPropertyName: "value", publicName: "ngpComboboxValue", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "ngpComboboxMultiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpComboboxDisabled", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "ngpComboboxCompareWith", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "ngpComboboxDropdownPlacement", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "ngpComboboxValueChange", openChange: "ngpComboboxOpenChange" }, host: { properties: { "attr.data-open": "state.open() ? \"\" : undefined", "attr.data-disabled": "state.disabled() ? \"\" : undefined", "attr.data-multiple": "state.multiple() ? \"\" : undefined" } }, providers: [provideComboboxState(), provideExitAnimationManager()], exportAs: ["ngpCombobox"], ngImport: i0 }); }
737
713
  }
738
714
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgpCombobox, decorators: [{
739
715
  type: Directive,
740
716
  args: [{
741
717
  selector: '[ngpCombobox]',
742
718
  exportAs: 'ngpCombobox',
743
- providers: [provideComboboxState()],
719
+ providers: [provideComboboxState(), provideExitAnimationManager()],
744
720
  host: {
745
721
  '[attr.data-open]': 'state.open() ? "" : undefined',
746
722
  '[attr.data-disabled]': 'state.disabled() ? "" : undefined',