Skip to content

Commit

Permalink
feat(overflow-menu): add overflow menu components
Browse files Browse the repository at this point in the history
  • Loading branch information
cal-smith committed Sep 7, 2018
1 parent d955f44 commit fe92fa8
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 38 deletions.
41 changes: 20 additions & 21 deletions src/dialog/dialog.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,31 @@ import { Dialog } from "./dialog.component";
import { DialogDirective } from "./dialog.directive";
import { DialogPlaceholderComponent } from "./dialog-placeholder.component";

import { Popover } from "./popover/popover.component";
import { PopoverDirective } from "./popover/popover.directive";
import { PopoverMenu } from "./popover/popover-menu.component";
import { PopoverMenuDirective } from "./popover/popover-menu.directive";

import { Tooltip } from "./tooltip/tooltip.component";
import { TooltipDirective } from "./tooltip/tooltip.directive";
import { EllipsisTooltipDirective } from "./tooltip/ellipsis-tooltip.directive";

import { OverflowMenu } from "./overflow-menu/overflow-menu.component";
import { OverflowMenuPane } from "./overflow-menu/overflow-menu-pane.component";
import { OverflowMenuDirective } from "./overflow-menu/overflow-menu.directive";
import { OverflowMenuOption } from "./overflow-menu/overflow-menu-option.component";

// exports
export { DialogService } from "./dialog.service";
export { DialogPlaceholderService } from "./dialog-placeholder.service";
export { Dialog } from "./dialog.component";
export { DialogDirective } from "./dialog.directive";
export { DialogPlaceholderComponent } from "./dialog-placeholder.component";

export { Popover } from "./popover/popover.component";
export { PopoverDirective } from "./popover/popover.directive";
export { PopoverMenu } from "./popover/popover-menu.component";
export { PopoverMenuDirective } from "./popover/popover-menu.directive";

export { Tooltip } from "./tooltip/tooltip.component";
export { TooltipDirective } from "./tooltip/tooltip.directive";
export { EllipsisTooltipDirective } from "./tooltip/ellipsis-tooltip.directive";

export { OverflowMenu } from "./overflow-menu/overflow-menu.component";
export { OverflowMenuPane } from "./overflow-menu/overflow-menu-pane.component";
export { OverflowMenuDirective } from "./overflow-menu/overflow-menu.directive";
export { OverflowMenuOption } from "./overflow-menu/overflow-menu-option.component";

// either provides a new instance of DialogPlaceholderService, or returns the parent
export function DIALOG_PLACEHOLDER_SERVICE_PROVIDER_FACTORY(parentService: DialogPlaceholderService) {
return parentService || new DialogPlaceholderService();
Expand All @@ -51,26 +51,26 @@ export const DIALOG_PLACEHOLDER_SERVICE_PROVIDER = {
@NgModule({
declarations: [
Dialog,
Popover,
PopoverMenu,
Tooltip,
OverflowMenu,
OverflowMenuPane,
DialogDirective,
PopoverDirective,
PopoverMenuDirective,
TooltipDirective,
EllipsisTooltipDirective,
OverflowMenuDirective,
OverflowMenuOption,
DialogPlaceholderComponent
],
exports: [
Dialog,
Popover,
PopoverMenu,
Tooltip,
OverflowMenu,
OverflowMenuPane,
DialogDirective,
PopoverDirective,
PopoverMenuDirective,
TooltipDirective,
EllipsisTooltipDirective,
OverflowMenuDirective,
OverflowMenuOption,
DialogPlaceholderComponent
],
providers: [
Expand All @@ -79,9 +79,8 @@ export const DIALOG_PLACEHOLDER_SERVICE_PROVIDER = {
],
entryComponents: [
Dialog,
Popover,
PopoverMenu,
Tooltip
Tooltip,
OverflowMenuPane
],
imports: [CommonModule, TranslateModule, StaticIconModule]
})
Expand Down
5 changes: 0 additions & 5 deletions src/dialog/dialog.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import {
EventEmitter,
Injector,
Component,
ComponentRef,
ComponentFactory,
ComponentFactoryResolver,
Injectable,
ApplicationRef,
ViewContainerRef,
Host
} from "@angular/core";
import { Subscription } from "rxjs";
import { DialogConfig } from "./dialog-config.interface";
import { DialogPlaceholderService } from "./dialog-placeholder.service";
import { Popover } from "..";


/**
* `Dialog` object to be injected into other components.
Expand Down
66 changes: 66 additions & 0 deletions src/dialog/overflow-menu/overflow-menu-option.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
HostBinding,
Component,
Input,
ElementRef,
AfterViewInit,
} from "@angular/core";

/**
* `OverflowMenuOption` represents a single option in an overflow menu
*
* Presently it has three possible states - normal, disabled, and danger:
* ```
* <ibm-overflow-menu-option>Simple option</ibm-overflow-menu-option>
* <ibm-overflow-menu-option disabled="true">Disabled</ibm-overflow-menu-option>
* <ibm-overflow-menu-option type="danger">Danger option</ibm-overflow-menu-option>
* ```
*
* For content that expands beyod the overflow menu `OverflowMenuOption` automatically adds a title attribute.
*/
@Component({
selector: "ibm-overflow-menu-option",
template: `
<button
class="bx--overflow-menu-options__btn"
[ngClass]="{
'bx--overflow-menu-options__option--danger': type === 'danger',
'bx--overflow-menu-options__option--disabled': disabled
}"
[tabindex]="(disabled?-1:null)"
[title]="(titleEnabled?getContent():'')">
<ng-content></ng-content>
</button>
`
})
export class OverflowMenuOption implements AfterViewInit {
@HostBinding("class") optionClass = "bx--overflow-menu-options__option";
@HostBinding("attr.role") role = "list-item";

/**
* toggles between `normal` and `danger` states
*/
@Input() type: "normal" | "danger" = "normal";
/**
* disable/enable interactions
*/
@Input() disabled = false;

public titleEnabled = false;

constructor(private elementRef: ElementRef) {}

ngAfterViewInit() {
const button = this.elementRef.nativeElement.querySelector("button");
if (button.scrollWidth > button.offsetWidth) {
this.titleEnabled = true;
}
}

/**
* Returns the text content projected into the component
*/
getContent(): string {
return this.elementRef.nativeElement.querySelector("button").textContent;
}
}
27 changes: 27 additions & 0 deletions src/dialog/overflow-menu/overflow-menu-pane.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
Component,
HostBinding
} from "@angular/core";
import { Dialog } from "../dialog.component";

/**
* Extend the `Dialog` component to create an overflow menu.
*
* Not used directly. See overflow-menu.component and overflow-menu.directive for more
*/
@Component({
selector: "ibm-overflow-menu-pane",
template: `
<div #dialog>
<ul
class="bx--overflow-menu-options bx--overflow-menu-options--open"
tabindex="-1">
<ng-template
[ngTemplateOutlet]="dialogConfig.content"
[ngTemplateOutletContext]="{overflowMenu: this}">
</ng-template>
</ul>
</div>
`
})
export class OverflowMenuPane extends Dialog {}
42 changes: 42 additions & 0 deletions src/dialog/overflow-menu/overflow-menu.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {
Component,
HostBinding,
Input,
TemplateRef
} from "@angular/core";

/**
* The OverFlow menu component encapsulates the OverFlowMenu directive, and the menu iconography into one convienent component
*
* html:
* ```
* <ibm-overflow-menu [options]="overflowContent"></ibm-overflow-menu>
* <ng-template #overflowContent>
* <ibm-overflow-menu-option>Option 1</ibm-overflow-menu-option>
* <ibm-overflow-menu-option>Option 2</ibm-overflow-menu-option>
* </ng-template>
* ```
*/
@Component({
selector: "ibm-overflow-menu",
template: `
<div
[ibmOverflowMenu]="options"
class="bx--overflow-menu"
style="display: block;">
<svg class="bx--overflow-menu__icon" width="3" height="15" viewBox="0 0 3 15">
<g fill-rule="evenodd">
<circle cx="1.5" cy="1.5" r="1.5" />
<circle cx="1.5" cy="7.5" r="1.5" />
<circle cx="1.5" cy="13.5" r="1.5" />
</g>
</svg>
</div>
`
})
export class OverflowMenu {
/**
* TemplateRef of `OverflowMenuOption`s (or compliant HTML) to render in the `OverflowMenuPane`s
*/
@Input() options: TemplateRef<any>;
}
59 changes: 59 additions & 0 deletions src/dialog/overflow-menu/overflow-menu.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
Directive,
ElementRef,
ViewContainerRef,
ContentChildren,
QueryList,
AfterViewInit,
ViewChildren,
Input,
TemplateRef
} from "@angular/core";
import { DialogDirective } from "./../dialog.directive";
import { DialogService } from "./../dialog.service";
import { OverflowMenuOption } from "./overflow-menu-option.component";
import { OverflowMenuPane } from "./overflow-menu-pane.component";


/**
* Directive for extending `Dialog` to create overflow menus.
*
* class: OverflowMenuDirective (extends DialogDirective)
*
*
* selector: `ibmOverflowMenu`
*
*
* ```html
* <div [ibmOverflowMenu]="templateRef"></div>
* <ng-template #templateRef>
* <!-- overflow menu options here -->
* </ng-template>
* ```
*/
@Directive({
selector: "[ibmOverflowMenu]",
exportAs: "ibmOverflowMenu",
providers: [
DialogService
]
})
export class OverflowMenuDirective extends DialogDirective {
@Input() ibmOverflowMenu: TemplateRef<any>;

/**
* Creates an instance of `OverflowMenuDirective`.
*/
constructor(
protected elementRef: ElementRef,
protected viewContainerRef: ViewContainerRef,
protected dialogService: DialogService
) {
super(elementRef, viewContainerRef, dialogService);
dialogService.create(OverflowMenuPane);
}

onDialogInit() {
this.dialogConfig.content = this.ibmOverflowMenu;
}
}
33 changes: 33 additions & 0 deletions src/dialog/overflow-menu/overflow-menu.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { storiesOf, moduleMetadata } from "@storybook/angular";

import { TranslateModule } from "@ngx-translate/core";

import { DialogModule } from "../../";

storiesOf("Overflow Menu", module)
.addDecorator(
moduleMetadata({
imports: [
DialogModule,
TranslateModule.forRoot()
],
})
)
.add("Basic", () => ({
template: `
<ibm-overflow-menu [options]="overflowContent"></ibm-overflow-menu>
<ng-template #overflowContent>
<ibm-overflow-menu-option>
An example option that is really long to show what should be done to handle long text
</ibm-overflow-menu-option>
<ibm-overflow-menu-option>Option 2</ibm-overflow-menu-option>
<li class="bx--overflow-menu-options__option">
<button class="bx--overflow-menu-options__btn">A fully custom option</button>
</li>
<ibm-overflow-menu-option>Option 4</ibm-overflow-menu-option>
<ibm-overflow-menu-option disabled="true">Disabled</ibm-overflow-menu-option>
<ibm-overflow-menu-option type="danger">Danger option</ibm-overflow-menu-option>
</ng-template>
<ibm-dialog-placeholder></ibm-dialog-placeholder>
`
}));
12 changes: 0 additions & 12 deletions src/dialog/tooltip/tooltip.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,4 @@ export class Tooltip extends Dialog {
onDialogInit() {
this.hasContentTemplate = this.dialogConfig.content instanceof TemplateRef;
}

/**
* Set the class of the `Tooltip`.
* @returns null
* @memberof Tooltip
*/
public getClass() {
if (this.dialogConfig.type) {
return `tooltip--${this.dialogConfig.type}-${this.placement}`;
}
return `tooltip--${this.placement}`;
}
}

0 comments on commit fe92fa8

Please sign in to comment.