Skip to content

Commit 3b90e48

Browse files
authored
Merge pull request #369 from cal-smith/uishell
feat(ui-shell): add ui-shell components
2 parents 53837e8 + 8bc8fd7 commit 3b90e48

13 files changed

+666
-8
lines changed

src/i18n/en.json

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,6 @@
9898
"PLACEHOLDER": "Search",
9999
"CLEAR_BUTTON": "Clear search input"
100100
},
101-
"SIDENAV": {
102-
"NAV_LABEL": "Side navigation"
103-
},
104101
"PAGINATION": {
105102
"ITEMS_PER_PAGE": "Items per page:",
106103
"OPEN_LIST_OF_OPTIONS": "Open list of options",
@@ -136,11 +133,16 @@
136133
"OFF": "Off",
137134
"ON": "On"
138135
},
139-
"TOPNAV": {
140-
"SKIP_TO_MAIN": "Skip to main content",
141-
"HAMBURGER": {
142-
"TOGGLE": "Toggle primary navigation menu",
143-
"TITLE": "Primary navigation menu"
136+
"UI_SHELL": {
137+
"SKIP_TO": "Skip to content",
138+
"HEADER": {
139+
"MENU": "Open menu"
140+
},
141+
"SIDE_NAV": {
142+
"LABEL": "Side navigation",
143+
"SWITCHER": "Switcher",
144+
"TOGGLE_OPEN": "Open",
145+
"TOGGLE_CLOSE": "Close"
144146
}
145147
}
146148
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Component, Input } from "@angular/core";
2+
3+
@Component({
4+
selector: "ibm-header-action",
5+
template: `
6+
<button
7+
class="bx--header__action"
8+
[attr.aria-label]="title"
9+
[title]="title">
10+
<ng-content></ng-content>
11+
</button>
12+
`
13+
})
14+
export class HeaderAction {
15+
@Input() title;
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Component, HostBinding } from "@angular/core";
2+
3+
@Component({
4+
selector: "ibm-header-global",
5+
template: `
6+
<ng-content></ng-content>
7+
`
8+
})
9+
export class HeaderGlobal {
10+
@HostBinding("class.bx--header__global") hostClass = true;
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Component } from "@angular/core";
2+
3+
@Component({
4+
selector: "ibm-header-item",
5+
template: `
6+
<li style="height: 100%">
7+
<a class="bx--header__menu-item" href="javascript:void(0)" role="menuitem" tabindex="0">
8+
<ng-content></ng-content>
9+
</a>
10+
</li>
11+
`
12+
})
13+
export class HeaderItem { }
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Component, Input, HostBinding, HostListener } from "@angular/core";
2+
3+
@Component({
4+
selector: "ibm-header-menu",
5+
template: `
6+
<li
7+
class="bx--header__submenu"
8+
style="height: 100%">
9+
<a
10+
class="bx--header__menu-item bx--header__menu-title"
11+
href="javascript:void(0)"
12+
role="menuitem"
13+
tabindex="0"
14+
aria-haspopup="true"
15+
[attr.aria-expanded]="expanded">
16+
{{title}}
17+
<svg class="bx--header__menu-arrow" width="12" height="7" aria-hidden="true">
18+
<path d="M6.002 5.55L11.27 0l.726.685L6.003 7 0 .685.726 0z" />
19+
</svg>
20+
</a>
21+
<ul class="bx--header__menu" role="menu" [attr.aria-label]="title">
22+
<ng-content></ng-content>
23+
</ul>
24+
</li>
25+
`
26+
})
27+
export class HeaderMenu {
28+
@Input() title;
29+
30+
expanded = false;
31+
32+
@HostListener("mouseover")
33+
onMouseOver() {
34+
this.expanded = true;
35+
}
36+
37+
@HostListener("mouseout")
38+
onMouseOut() {
39+
this.expanded = false;
40+
}
41+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Component, HostBinding } from "@angular/core";
2+
3+
@Component({
4+
selector: "ibm-header-navigation",
5+
template: `
6+
<nav class="bx--header__nav">
7+
<ul class="bx--header__menu-bar" role="menubar">
8+
<ng-content></ng-content>
9+
</ul>
10+
</nav>
11+
`
12+
})
13+
export class HeaderNavigation {
14+
@HostBinding("style.height.%") height = 100;
15+
}

src/ui-shell/header.component.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Component, Input, HostBinding } from "@angular/core";
2+
import { I18n } from "./../i18n/i18n.module";
3+
4+
@Component({
5+
selector: "ibm-header",
6+
template: `
7+
<header
8+
class="bx--header"
9+
role="banner"
10+
[attr.aria-label]="brand + ' ' + name">
11+
<a
12+
class="bx--skip-to-content"
13+
[href]="skipTo"
14+
tabindex="0">
15+
{{ i18n.get("UI_SHELL.SKIP_TO") | async }}
16+
</a>
17+
<button
18+
class="bx--header__menu-trigger bx--header__action"
19+
[attr.aria-label]="i18n.get('UI_SHELL.HEADER.MENU') | async"
20+
[attr.title]="i18n.get('UI_SHELL.HEADER.MENU') | async">
21+
<svg aria-hidden="true" width="20" height="20" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
22+
<path d="M4 6h24v2H4zm0 18h24v2H4zm0-9h24v2H4z" />
23+
</svg>
24+
</button>
25+
<a class="bx--header__name" href="#">
26+
<span class="bx--header__name--prefix">{{brand}}&nbsp;</span>
27+
{{name}}
28+
</a>
29+
<ng-content></ng-content>
30+
</header>
31+
`
32+
})
33+
export class Header {
34+
@Input() skipTo: string;
35+
@Input() name: string;
36+
@Input() brand = "IBM";
37+
38+
@HostBinding("class.bx--header") hostClass = true;
39+
40+
constructor(public i18n: I18n) { }
41+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Component, Input } from "@angular/core";
2+
import { ListItem } from "./../dropdown/dropdown.module";
3+
import { I18n } from "./../i18n/i18n.module";
4+
5+
@Component({
6+
selector: "ibm-sidenav-header",
7+
template: `
8+
<header class="bx--side-nav__header">
9+
<div class="bx--side-nav__icon">
10+
<ng-content select="[icon]"></ng-content>
11+
</div>
12+
<div class="bx--side-nav__details">
13+
<h2 class="bx--side-nav__title" [title]="title">{{title}}</h2>
14+
<div class="bx--side-nav__switcher">
15+
<label class="bx--assistive-text" [for]="switcherId">
16+
{{i18n.get('UI_SHELL.SIDE_NAV.SWITCHER') | async}}
17+
</label>
18+
<select [id]="switcherId" class="bx--side-nav__select">
19+
<option class="bx--side-nav__option" disabled="" value="" selected="" hidden="">
20+
{{i18n.get('UI_SHELL.SIDE_NAV.SWITCHER') | async}}
21+
</option>
22+
<option
23+
*ngFor="let option of options"
24+
class="bx--side-nav__option"
25+
[value]="(option.value ? option.value : option.content)">
26+
{{option.content}}
27+
</option>
28+
</select>
29+
<div class="bx--side-nav__switcher-chevron">
30+
<svg
31+
focusable="false"
32+
preserveAspectRatio="xMidYMid meet"
33+
style="will-change: transform;"
34+
xmlns="http://www.w3.org/2000/svg"
35+
width="20"
36+
height="20"
37+
viewBox="0 0 32 32"
38+
aria-hidden="true">
39+
<path d="M16 22L6 12l1.4-1.4 8.6 8.6 8.6-8.6L26 12z"></path>
40+
</svg>
41+
</div>
42+
</div>
43+
</div>
44+
</header>
45+
`
46+
})
47+
export class SideNavHeader {
48+
@Input() title: string;
49+
@Input() options: ListItem[];
50+
public switcherId = "sidenav-switcher";
51+
52+
constructor(public i18n: I18n) { }
53+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Component, Input } from "@angular/core";
2+
3+
@Component({
4+
selector: "ibm-sidenav-item",
5+
template: `
6+
<li [ngClass]="{
7+
'bx--side-nav__item': !isSubMenu,
8+
'bx--side-nav__menu-item': isSubMenu
9+
}"
10+
[attr.role]="(isSubMenu ? 'none' : null)">
11+
<a
12+
class="bx--side-nav__link"
13+
href="javascript:void(0)"
14+
[attr.role]="(isSubMenu ? 'menuitem' : null)"
15+
[attr.aria-current]="(active ? 'page' : null)">
16+
<div *ngIf="!isSubMenu" class="bx--side-nav__icon">
17+
<ng-content select="[icon]"></ng-content>
18+
</div>
19+
<span class="bx--side-nav__link-text">
20+
<ng-content></ng-content>
21+
</span>
22+
</a>
23+
</li>
24+
`
25+
})
26+
export class SideNavItem {
27+
@Input() active = false;
28+
isSubMenu = false;
29+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {
2+
AfterContentInit,
3+
Component,
4+
ContentChildren,
5+
Input,
6+
QueryList
7+
} from "@angular/core";
8+
import { SideNavItem } from "./ui-shell.module";
9+
10+
@Component({
11+
selector: "ibm-sidenav-menu",
12+
template: `
13+
<li class="bx--side-nav__item">
14+
<button
15+
(click)="toggle()"
16+
class="bx--side-nav__submenu"
17+
aria-haspopup="true"
18+
[attr.aria-expanded]="expanded"
19+
type="button">
20+
<div class="bx--side-nav__icon">
21+
<ng-content select="[icon]"></ng-content>
22+
</div>
23+
<span class="bx--side-nav__submenu-title">{{title}}</span>
24+
<div class="bx--side-nav__icon bx--side-nav__icon--small bx--side-nav__submenu-chevron">
25+
<svg
26+
focusable="false"
27+
preserveAspectRatio="xMidYMid meet"
28+
style="will-change: transform;"
29+
xmlns="http://www.w3.org/2000/svg"
30+
width="20"
31+
height="20"
32+
viewBox="0 0 32 32"
33+
aria-hidden="true">
34+
<path d="M16 22L6 12l1.4-1.4 8.6 8.6 8.6-8.6L26 12z"></path>
35+
</svg>
36+
</div>
37+
</button>
38+
<ul class="bx--side-nav__menu" role="menu">
39+
<ng-content></ng-content>
40+
</ul>
41+
</li>
42+
`
43+
})
44+
export class SideNavMenu implements AfterContentInit {
45+
@Input() title;
46+
47+
@ContentChildren(SideNavItem) sidenavItems: QueryList<SideNavItem>;
48+
49+
@Input() expanded = false;
50+
51+
ngAfterContentInit() {
52+
this.sidenavItems.forEach(item => {
53+
item.isSubMenu = true;
54+
});
55+
}
56+
57+
toggle() {
58+
this.expanded = !this.expanded;
59+
}
60+
}

0 commit comments

Comments
 (0)