Skip to content

Commit

Permalink
feat(context-menu): add scroll strategy provider (#541)
Browse files Browse the repository at this point in the history
  • Loading branch information
Igor Milly authored and GitHub Enterprise committed Mar 14, 2022
1 parent 7eb4634 commit 10d6c2d
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ContextMenuIndicatorExampleComponent } from './context-menu-indicator/c
import { ContextMenuLazyExampleComponent } from './context-menu-lazy/context-menu-lazy-example';
import { ContextMenuNestedExampleComponent } from './context-menu-nested/context-menu-nested-example';
import { ContextMenuProgrammaticExampleComponent } from './context-menu-programmatic/context-menu-programmatic-example';
import { ContextMenuScrollStrategyProviderExampleComponent } from './context-menu-scroll-strategy-provider/context-menu-scroll-strategy-provider-example';
import { ContextMenuScrollStrategyExampleComponent } from './context-menu-scroll-strategy/context-menu-scroll-strategy-example';

const EXAMPLES = [
Expand All @@ -28,6 +29,7 @@ const EXAMPLES = [
ContextMenuScrollStrategyExampleComponent,
ContextMenuIndicatorExampleComponent,
ContextMenuCursorModeExampleComponent,
ContextMenuScrollStrategyProviderExampleComponent,
];

@NgModule({
Expand Down Expand Up @@ -56,6 +58,8 @@ export class ContextExamplesModule {
ContextMenuProgrammaticExampleComponent,
'context-menu-scroll-strategy':
ContextMenuScrollStrategyExampleComponent,
'context-menu-scroll-strategy-provider':
ContextMenuScrollStrategyProviderExampleComponent,
'context-menu-indicator': ContextMenuIndicatorExampleComponent,
'context-menu-cursor-mode': ContextMenuCursorModeExampleComponent,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<nx-context-menu #menu="nxContextMenu">
<button nxContextMenuItem type="button">Settings</button>
<button nxContextMenuItem type="button">Download</button>
<button nxContextMenuItem type="button">Help</button>
</nx-context-menu>

<button
nxIconButton="tertiary small"
[nxContextMenuTriggerFor]="menu"
aria-label="Open menu"
type="button"
>
<nx-icon aria-hidden="true" name="ellipsis-h"></nx-icon>
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Overlay, ScrollStrategy } from '@angular/cdk/overlay';
import { Component } from '@angular/core';
import { NX_CONTEXT_MENU_SCROLL_STRATEGY } from '@aposin/ng-aquila/context-menu';

function scrollStrategyFactory(overlay: Overlay): () => ScrollStrategy {
return () => overlay.scrollStrategies.close({ threshold: 100 });
}

/**
* @title Scroll Strategy Provider Example
*/
@Component({
selector: 'context-menu-scroll-strategy-provider-example',
templateUrl: './context-menu-scroll-strategy-provider-example.html',
styleUrls: ['./context-menu-scroll-strategy-provider-example.css'],
providers: [
{
provide: NX_CONTEXT_MENU_SCROLL_STRATEGY,
useFactory: scrollStrategyFactory,
deps: [Overlay],
},
],
})
export class ContextMenuScrollStrategyProviderExampleComponent {}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,22 @@ import { LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
import { ConnectedPosition, FlexibleConnectedPositionStrategy, Overlay, OverlayConfig, OverlayRef, ScrollStrategy } from '@angular/cdk/overlay';
import { _getEventTarget } from '@angular/cdk/platform';
import { TemplatePortal } from '@angular/cdk/portal';
import { AfterContentInit, Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Optional, Output, Self, ViewContainerRef } from '@angular/core';
import {
AfterContentInit,
ChangeDetectorRef,
Directive,
ElementRef,
EventEmitter,
Inject,
InjectionToken,
Input,
OnDestroy,
OnInit,
Optional,
Output,
Self,
ViewContainerRef,
} from '@angular/core';
import { NxTriggerButton } from '@aposin/ng-aquila/overlay';
import { asapScheduler, fromEvent, merge, Observable, of as observableOf, Subscription } from 'rxjs';
import { delay, filter, map, take, takeUntil } from 'rxjs/operators';
Expand All @@ -24,6 +39,21 @@ export type NxContextMenuScrollStrategy = 'close' | 'reposition';

export type NxContextMenuMode = 'button' | 'cursor';

/** Injection token that determines the scroll handling while a context-menu is open. */
export const NX_CONTEXT_MENU_SCROLL_STRATEGY = new InjectionToken<() => ScrollStrategy>('nx-context-menu-scroll-strategy');

/** @docs-private */
export function NX_CONTEXT_MENU_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay): () => ScrollStrategy {
return () => overlay.scrollStrategies.reposition();
}

/** @docs-private */
export const NX_CONTEXT_MENU_SCROLL_STRATEGY_PROVIDER = {
provide: NX_CONTEXT_MENU_SCROLL_STRATEGY,
useFactory: NX_CONTEXT_MENU_SCROLL_STRATEGY_PROVIDER_FACTORY,
deps: [Overlay],
};

interface Point {
x: number;
y: number;
Expand Down Expand Up @@ -54,7 +84,9 @@ export class NxContextMenuTriggerDirective implements AfterContentInit, OnInit,
private _contextMenuCloseSubscription = Subscription.EMPTY;
private _dirChangeSubscription = Subscription.EMPTY;
private _documentClickObservable: Observable<MouseEvent>;
private _scrollStrategy: () => ScrollStrategy;

/** Strategy factory that will be used to handle scrolling while the context-menu panel is open. */
private _scrollStrategyFactory = this._defaultScrollStrategyFactory;

/** References the context menu instance that the trigger is associated with. */
@Input('nxContextMenuTriggerFor')
Expand Down Expand Up @@ -83,13 +115,17 @@ export class NxContextMenuTriggerDirective implements AfterContentInit, OnInit,
private _contextMenu!: NxContextMenuComponent;

@Input()
set scrollStrategy(value: NxContextMenuScrollStrategy) {
if (value === 'close') {
this._scrollStrategy = this._overlay.scrollStrategies.close;
} else {
this._scrollStrategy = this._overlay.scrollStrategies.reposition;
set scrollStrategy(value: NxContextMenuScrollStrategy | null | undefined) {
if (this.#scrollStrategy !== value) {
this.#scrollStrategy = value;
this._scrollStrategyFactory = value ? this.getScrollStrtegyFactory(value) : this._defaultScrollStrategyFactory;
this._cdr.markForCheck();
}
}
get scrollStrategy(): NxContextMenuScrollStrategy | null | undefined {
return this.#scrollStrategy;
}
#scrollStrategy?: NxContextMenuScrollStrategy | null;

/** Whether the context menu is open. */
get contextMenuOpen(): boolean {
Expand Down Expand Up @@ -128,12 +164,12 @@ export class NxContextMenuTriggerDirective implements AfterContentInit, OnInit,
private _contextMenuItemInstance: NxContextMenuItemComponent,
@Optional() private _dir: Directionality,
@Optional() @Self() private _triggerButton: NxTriggerButton,
@Inject(NX_CONTEXT_MENU_SCROLL_STRATEGY) private _defaultScrollStrategyFactory: () => ScrollStrategy,
private _cdr: ChangeDetectorRef,
) {
if (_contextMenuItemInstance) {
_contextMenuItemInstance._triggersSubmenu = this.triggersSubmenu();
}

this._scrollStrategy = this._overlay.scrollStrategies.reposition;
this._documentClickObservable = fromEvent<MouseEvent>(document, 'click');
}

Expand Down Expand Up @@ -218,6 +254,15 @@ export class NxContextMenuTriggerDirective implements AfterContentInit, OnInit,
this.contextMenu.closed.emit();
}

private getScrollStrtegyFactory(scrollStrategy: NxContextMenuScrollStrategy): () => ScrollStrategy {
switch (scrollStrategy) {
case 'close':
return this._overlay.scrollStrategies.close;
default:
return this._overlay.scrollStrategies.reposition;
}
}

/** Closes the context menu and does the necessary cleanup. */
private _destroyMenu() {
if (!this._overlayRef || !this.contextMenuOpen) {
Expand Down Expand Up @@ -325,7 +370,7 @@ export class NxContextMenuTriggerDirective implements AfterContentInit, OnInit,
.withLockedPosition()
.withFlexibleDimensions(false)
.withTransformOriginOn('.nx-context-menu'),
scrollStrategy: this._scrollStrategy(),
scrollStrategy: this._scrollStrategyFactory(),
direction: this._dir,
});
}
Expand Down
11 changes: 11 additions & 0 deletions projects/ng-aquila/src/context-menu/context-menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ You can set the mode of the context menu to `cursor` to be able to open it via r

<!-- example(context-menu-cursor-mode) -->

### Global Settings

If you want to use a custom scroll strategy for all of your context menus, you can use the `NX_CONTEXT_MENU_SCROLL_STRATEGY` injection token with a factory provider. The `Overlay` service from `@angular/cdk/overlay` offers 4 different scroll strategy options:

- **reposition:** allow background scroll, the overlay moves with the background (default).
- **close:** allow background scroll, closes the overlay on scroll.
- **block:** disallow background scroll, the overlay does not move.
- **noop:** allow background scroll, the overlay does not move.

<!-- example(context-menu-scroll-strategy-provider) -->

### Keyboard interaction

- DOWN_ARROW: Focuses the next menu item
Expand Down
3 changes: 2 additions & 1 deletion projects/ng-aquila/src/context-menu/context-menu.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import { NxIconModule } from '@aposin/ng-aquila/icon';
import { NxContextMenuComponent } from './context-menu.component';
import { NxContextMenuContentDirective } from './context-menu-content.directive';
import { NxContextMenuItemComponent } from './context-menu-item.component';
import { NxContextMenuTriggerDirective } from './context-menu-trigger.directive';
import { NX_CONTEXT_MENU_SCROLL_STRATEGY_PROVIDER, NxContextMenuTriggerDirective } from './context-menu-trigger.directive';

const EXPORTED_MODULES = [NxContextMenuComponent, NxContextMenuContentDirective, NxContextMenuItemComponent, NxContextMenuTriggerDirective];

@NgModule({
imports: [CommonModule, OverlayModule, NxIconModule],
exports: EXPORTED_MODULES,
declarations: EXPORTED_MODULES,
providers: [NX_CONTEXT_MENU_SCROLL_STRATEGY_PROVIDER],
})
export class NxContextMenuModule {}

0 comments on commit 10d6c2d

Please sign in to comment.