Skip to content

Commit cc5a776

Browse files
authored
Merge pull request #49 from cal-smith/master
feat: add overflow menu and associated components
2 parents ca134a1 + be07152 commit cc5a776

File tree

10 files changed

+238
-56
lines changed

10 files changed

+238
-56
lines changed

src/dialog/dialog.module.ts

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,31 @@ import { Dialog } from "./dialog.component";
1111
import { DialogDirective } from "./dialog.directive";
1212
import { DialogPlaceholderComponent } from "./dialog-placeholder.component";
1313

14-
import { Popover } from "./popover/popover.component";
15-
import { PopoverDirective } from "./popover/popover.directive";
16-
import { PopoverMenu } from "./popover/popover-menu.component";
17-
import { PopoverMenuDirective } from "./popover/popover-menu.directive";
18-
1914
import { Tooltip } from "./tooltip/tooltip.component";
2015
import { TooltipDirective } from "./tooltip/tooltip.directive";
2116
import { EllipsisTooltipDirective } from "./tooltip/ellipsis-tooltip.directive";
2217

18+
import { OverflowMenu } from "./overflow-menu/overflow-menu.component";
19+
import { OverflowMenuPane } from "./overflow-menu/overflow-menu-pane.component";
20+
import { OverflowMenuDirective } from "./overflow-menu/overflow-menu.directive";
21+
import { OverflowMenuOption } from "./overflow-menu/overflow-menu-option.component";
22+
2323
// exports
2424
export { DialogService } from "./dialog.service";
2525
export { DialogPlaceholderService } from "./dialog-placeholder.service";
2626
export { Dialog } from "./dialog.component";
2727
export { DialogDirective } from "./dialog.directive";
2828
export { DialogPlaceholderComponent } from "./dialog-placeholder.component";
2929

30-
export { Popover } from "./popover/popover.component";
31-
export { PopoverDirective } from "./popover/popover.directive";
32-
export { PopoverMenu } from "./popover/popover-menu.component";
33-
export { PopoverMenuDirective } from "./popover/popover-menu.directive";
34-
3530
export { Tooltip } from "./tooltip/tooltip.component";
3631
export { TooltipDirective } from "./tooltip/tooltip.directive";
3732
export { EllipsisTooltipDirective } from "./tooltip/ellipsis-tooltip.directive";
3833

34+
export { OverflowMenu } from "./overflow-menu/overflow-menu.component";
35+
export { OverflowMenuPane } from "./overflow-menu/overflow-menu-pane.component";
36+
export { OverflowMenuDirective } from "./overflow-menu/overflow-menu.directive";
37+
export { OverflowMenuOption } from "./overflow-menu/overflow-menu-option.component";
38+
3939
// either provides a new instance of DialogPlaceholderService, or returns the parent
4040
export function DIALOG_PLACEHOLDER_SERVICE_PROVIDER_FACTORY(parentService: DialogPlaceholderService) {
4141
return parentService || new DialogPlaceholderService();
@@ -51,26 +51,26 @@ export const DIALOG_PLACEHOLDER_SERVICE_PROVIDER = {
5151
@NgModule({
5252
declarations: [
5353
Dialog,
54-
Popover,
55-
PopoverMenu,
5654
Tooltip,
55+
OverflowMenu,
56+
OverflowMenuPane,
5757
DialogDirective,
58-
PopoverDirective,
59-
PopoverMenuDirective,
6058
TooltipDirective,
6159
EllipsisTooltipDirective,
60+
OverflowMenuDirective,
61+
OverflowMenuOption,
6262
DialogPlaceholderComponent
6363
],
6464
exports: [
6565
Dialog,
66-
Popover,
67-
PopoverMenu,
6866
Tooltip,
67+
OverflowMenu,
68+
OverflowMenuPane,
6969
DialogDirective,
70-
PopoverDirective,
71-
PopoverMenuDirective,
7270
TooltipDirective,
7371
EllipsisTooltipDirective,
72+
OverflowMenuDirective,
73+
OverflowMenuOption,
7474
DialogPlaceholderComponent
7575
],
7676
providers: [
@@ -79,9 +79,8 @@ export const DIALOG_PLACEHOLDER_SERVICE_PROVIDER = {
7979
],
8080
entryComponents: [
8181
Dialog,
82-
Popover,
83-
PopoverMenu,
84-
Tooltip
82+
Tooltip,
83+
OverflowMenuPane
8584
],
8685
imports: [CommonModule, TranslateModule, StaticIconModule]
8786
})

src/dialog/dialog.service.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
import {
22
EventEmitter,
33
Injector,
4-
Component,
54
ComponentRef,
65
ComponentFactory,
76
ComponentFactoryResolver,
87
Injectable,
9-
ApplicationRef,
108
ViewContainerRef,
11-
Host
129
} from "@angular/core";
1310
import { Subscription } from "rxjs";
1411
import { DialogConfig } from "./dialog-config.interface";
1512
import { DialogPlaceholderService } from "./dialog-placeholder.service";
16-
import { Popover } from "..";
17-
1813

1914
/**
2015
* `Dialog` object to be injected into other components.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {
2+
HostBinding,
3+
Component,
4+
Input,
5+
ElementRef
6+
} from "@angular/core";
7+
8+
/**
9+
* `OverflowMenuOption` represents a single option in an overflow menu
10+
*
11+
* Presently it has three possible states - normal, disabled, and danger:
12+
* ```
13+
* <ibm-overflow-menu-option>Simple option</ibm-overflow-menu-option>
14+
* <ibm-overflow-menu-option disabled="true">Disabled</ibm-overflow-menu-option>
15+
* <ibm-overflow-menu-option type="danger">Danger option</ibm-overflow-menu-option>
16+
* ```
17+
*
18+
* For content that expands beyod the overflow menu `OverflowMenuOption` automatically adds a title attribute.
19+
*/
20+
@Component({
21+
selector: "ibm-overflow-menu-option",
22+
template: `
23+
<button
24+
class="bx--overflow-menu-options__btn"
25+
[ngClass]="{
26+
'bx--overflow-menu-options__option--danger': type === 'danger',
27+
'bx--overflow-menu-options__option--disabled': disabled
28+
}"
29+
[tabindex]="(disabled ? -1 : null)"
30+
[title]="(titleEnabled ? content : '')">
31+
<ng-content></ng-content>
32+
</button>
33+
`
34+
})
35+
export class OverflowMenuOption {
36+
@HostBinding("class") optionClass = "bx--overflow-menu-options__option";
37+
@HostBinding("attr.role") role = "list-item";
38+
39+
/**
40+
* toggles between `normal` and `danger` states
41+
*/
42+
@Input() type: "normal" | "danger" = "normal";
43+
/**
44+
* disable/enable interactions
45+
*/
46+
@Input() disabled = false;
47+
48+
constructor(private elementRef: ElementRef) {}
49+
50+
/**
51+
* Returns true if the content string is longer than the width of the containing button
52+
*
53+
* note: getter ties into the view check cycle so we always get an accurate value
54+
*/
55+
get titleEnabled() {
56+
const button = this.elementRef.nativeElement.querySelector("button");
57+
if (button.scrollWidth > button.offsetWidth) {
58+
return true;
59+
}
60+
return false;
61+
}
62+
63+
/**
64+
* Returns the text content projected into the component
65+
*/
66+
get content(): string {
67+
return this.elementRef.nativeElement.querySelector("button").textContent;
68+
}
69+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Component } from "@angular/core";
2+
import { Dialog } from "../dialog.component";
3+
4+
/**
5+
* Extend the `Dialog` component to create an overflow menu.
6+
*
7+
* Not used directly. See overflow-menu.component and overflow-menu.directive for more
8+
*/
9+
@Component({
10+
selector: "ibm-overflow-menu-pane",
11+
template: `
12+
<div #dialog>
13+
<ul
14+
class="bx--overflow-menu-options bx--overflow-menu-options--open"
15+
tabindex="-1">
16+
<ng-template
17+
[ngTemplateOutlet]="dialogConfig.content"
18+
[ngTemplateOutletContext]="{overflowMenu: this}">
19+
</ng-template>
20+
</ul>
21+
</div>
22+
`
23+
})
24+
export class OverflowMenuPane extends Dialog {}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Component } from "@angular/core";
2+
3+
/**
4+
* The OverFlow menu component encapsulates the OverFlowMenu directive, and the menu iconography into one convienent component
5+
*
6+
* html:
7+
* ```
8+
* <ibm-overflow-menu [options]="overflowContent"></ibm-overflow-menu>
9+
* <ng-template #overflowContent>
10+
* <ibm-overflow-menu-option>Option 1</ibm-overflow-menu-option>
11+
* <ibm-overflow-menu-option>Option 2</ibm-overflow-menu-option>
12+
* </ng-template>
13+
* ```
14+
*/
15+
@Component({
16+
selector: "ibm-overflow-menu",
17+
template: `
18+
<div
19+
[ibmOverflowMenu]="options"
20+
class="bx--overflow-menu"
21+
style="display: block;">
22+
<svg class="bx--overflow-menu__icon" width="3" height="15" viewBox="0 0 3 15">
23+
<g fill-rule="evenodd">
24+
<circle cx="1.5" cy="1.5" r="1.5" />
25+
<circle cx="1.5" cy="7.5" r="1.5" />
26+
<circle cx="1.5" cy="13.5" r="1.5" />
27+
</g>
28+
</svg>
29+
</div>
30+
<ng-template #options>
31+
<ng-content></ng-content>
32+
</ng-template>
33+
`
34+
})
35+
export class OverflowMenu {}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {
2+
Directive,
3+
ElementRef,
4+
ViewContainerRef,
5+
Input,
6+
TemplateRef
7+
} from "@angular/core";
8+
import { DialogDirective } from "./../dialog.directive";
9+
import { DialogService } from "./../dialog.service";
10+
import { OverflowMenuPane } from "./overflow-menu-pane.component";
11+
12+
13+
/**
14+
* Directive for extending `Dialog` to create overflow menus.
15+
*
16+
* class: OverflowMenuDirective (extends DialogDirective)
17+
*
18+
*
19+
* selector: `ibmOverflowMenu`
20+
*
21+
*
22+
* ```html
23+
* <div [ibmOverflowMenu]="templateRef"></div>
24+
* <ng-template #templateRef>
25+
* <!-- overflow menu options here -->
26+
* </ng-template>
27+
* ```
28+
*/
29+
@Directive({
30+
selector: "[ibmOverflowMenu]",
31+
exportAs: "ibmOverflowMenu",
32+
providers: [
33+
DialogService
34+
]
35+
})
36+
export class OverflowMenuDirective extends DialogDirective {
37+
@Input() ibmOverflowMenu: TemplateRef<any>;
38+
39+
/**
40+
* Creates an instance of `OverflowMenuDirective`.
41+
*/
42+
constructor(
43+
protected elementRef: ElementRef,
44+
protected viewContainerRef: ViewContainerRef,
45+
protected dialogService: DialogService
46+
) {
47+
super(elementRef, viewContainerRef, dialogService);
48+
dialogService.create(OverflowMenuPane);
49+
}
50+
51+
onDialogInit() {
52+
this.dialogConfig.content = this.ibmOverflowMenu;
53+
}
54+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { storiesOf, moduleMetadata } from "@storybook/angular";
2+
3+
import { TranslateModule } from "@ngx-translate/core";
4+
5+
import { DialogModule } from "../../";
6+
7+
storiesOf("Overflow Menu", module)
8+
.addDecorator(
9+
moduleMetadata({
10+
imports: [
11+
DialogModule,
12+
TranslateModule.forRoot()
13+
]
14+
})
15+
)
16+
.add("Basic", () => ({
17+
template: `
18+
<ibm-overflow-menu>
19+
<ibm-overflow-menu-option>
20+
An example option that is really long to show what should be done to handle long text
21+
</ibm-overflow-menu-option>
22+
<ibm-overflow-menu-option>Option 2</ibm-overflow-menu-option>
23+
<li class="bx--overflow-menu-options__option">
24+
<button class="bx--overflow-menu-options__btn">A fully custom option</button>
25+
</li>
26+
<ibm-overflow-menu-option>Option 4</ibm-overflow-menu-option>
27+
<ibm-overflow-menu-option disabled="true">Disabled</ibm-overflow-menu-option>
28+
<ibm-overflow-menu-option type="danger">Danger option</ibm-overflow-menu-option>
29+
</ibm-overflow-menu>
30+
<ibm-dialog-placeholder></ibm-dialog-placeholder>
31+
`
32+
}));
Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,12 @@
11
import {
22
Component,
3-
Input,
4-
Output,
5-
EventEmitter,
6-
OnInit,
7-
AfterViewInit,
8-
Injector,
9-
ElementRef,
103
TemplateRef,
11-
HostListener,
12-
ViewChild,
134
HostBinding
145
} from "@angular/core";
156
import { Dialog } from "./../dialog.component";
167

178
/**
189
* Extend the `Dialog` component to create a tooltip for exposing content.
19-
* @export
20-
* @class Tooltip
21-
* @extends {Dialog}
2210
*/
2311
@Component({
2412
selector: "ibm-tooltip",
@@ -46,28 +34,13 @@ export class Tooltip extends Dialog {
4634
@HostBinding("style.display") style = "inline-block";
4735
/**
4836
* Value is set to `true` if the `Tooltip` is to display a `TemplateRef` instead of a string.
49-
* @type {boolean}
50-
* @memberof Tooltip
5137
*/
5238
public hasContentTemplate = false;
5339

5440
/**
5541
* Check whether there is a template for the `Tooltip` content.
56-
* @memberof Tooltip
5742
*/
5843
onDialogInit() {
5944
this.hasContentTemplate = this.dialogConfig.content instanceof TemplateRef;
6045
}
61-
62-
/**
63-
* Set the class of the `Tooltip`.
64-
* @returns null
65-
* @memberof Tooltip
66-
*/
67-
public getClass() {
68-
if (this.dialogConfig.type) {
69-
return `tooltip--${this.dialogConfig.type}-${this.placement}`;
70-
}
71-
return `tooltip--${this.placement}`;
72-
}
7346
}

src/index.stories.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ storiesOf("Welcome", module).add("to Carbon Angular", () => ({
66
template: `
77
<h1>Carbon Components Angular</h1>
88
<h2>An Angular implementation of the Carbon Design System</h2>
9+
<a href="https://angular.carbondesignsystem.com/documentation">Documentation</a>
910
`,
10-
props: {},
11+
props: {}
1112
}));

0 commit comments

Comments
 (0)