Skip to content

Commit e971374

Browse files
authored
Merge branch 'master' into tooltip-icon
2 parents 7db1a47 + 0d2400c commit e971374

File tree

7 files changed

+333
-3
lines changed

7 files changed

+333
-3
lines changed

src/i18n/en.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@
131131
"BUTTON_ARIA_LEFT": "Go to the previous tab",
132132
"BUTTON_ARIA_RIGHT": "Go to the next tab"
133133
},
134+
"TILES": {
135+
"TILE": "tile",
136+
"EXPAND": "Expand",
137+
"COLLAPSE": "Collapse"
138+
},
134139
"TOGGLE": {
135140
"OFF": "Off",
136141
"ON": "On"
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {
2+
Component,
3+
HostBinding,
4+
Input,
5+
ViewChild,
6+
ElementRef,
7+
AfterContentInit
8+
} from "@angular/core";
9+
import { I18n } from "./../i18n/i18n.module";
10+
11+
@Component({
12+
selector: "ibm-expandable-tile",
13+
template: `
14+
<div
15+
class="bx--tile bx--tile--expandable"
16+
[ngClass]="{'bx--tile--is-expanded' : expanded}"
17+
[ngStyle]="{'max-height': expandedHeight + 'px'}"
18+
role="button"
19+
tabindex="0"
20+
(click)="onClick()">
21+
<button [attr.aria-label]="(expanded ? collapse : expand) | async" class="bx--tile__chevron">
22+
<svg *ngIf="!expanded" width="12" height="7" viewBox="0 0 12 7" role="img">
23+
<title>{{expand | async}}</title>
24+
<path fill-rule="nonzero" d="M6.002 5.55L11.27 0l.726.685L6.003 7 0 .685.726 0z"/>
25+
</svg>
26+
<svg *ngIf="expanded" width="12" height="7" viewBox="0 0 12 7" role="img">
27+
<title>{{collapse | async}}</title>
28+
<path fill-rule="nonzero" d="M6.002 5.55L11.27 0l.726.685L6.003 7 0 .685.726 0z"/>
29+
</svg>
30+
</button>
31+
<div class="bx--tile-content">
32+
<ng-content select=".bx--tile-content__above-the-fold"></ng-content>
33+
<ng-content select=".bx--tile-content__below-the-fold"></ng-content>
34+
</div>
35+
</div>
36+
`
37+
})
38+
export class ExpandableTile implements AfterContentInit {
39+
@Input() expanded = false;
40+
/**
41+
* Expects an object that contains some or all of:
42+
* ```
43+
* {
44+
* "EXPAND": "Expand",
45+
* "COLLAPSE": "Collapse",
46+
* }
47+
* ```
48+
*/
49+
@Input()
50+
set translations (value) {
51+
if (value.EXPAND) {
52+
this.expand.next(value.EXPAND);
53+
}
54+
if (value.COLLAPSE) {
55+
this.collapse.next(value.COLLAPSE);
56+
}
57+
}
58+
59+
tileMaxHeight = 0;
60+
element = this.elementRef.nativeElement;
61+
62+
expand = this.i18n.get("TILES.EXPAND");
63+
collapse = this.i18n.get("TILES.COLLAPSE");
64+
65+
constructor(protected i18n: I18n, protected elementRef: ElementRef) {}
66+
67+
ngAfterContentInit() {
68+
this.updateMaxHeight();
69+
}
70+
71+
get expandedHeight() {
72+
return this.tileMaxHeight + parseInt(getComputedStyle(this.element.querySelector(".bx--tile")).paddingBottom, 10);
73+
}
74+
75+
updateMaxHeight() {
76+
if (this.expanded) {
77+
this.tileMaxHeight = this.element.querySelector(".bx--tile-content").getBoundingClientRect().height;
78+
} else {
79+
this.tileMaxHeight = this.element.querySelector(".bx--tile-content__above-the-fold").getBoundingClientRect().height;
80+
}
81+
}
82+
83+
onClick() {
84+
this.expanded = !this.expanded;
85+
this.updateMaxHeight();
86+
}
87+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
Component,
3+
Input,
4+
Output,
5+
EventEmitter,
6+
ViewChild
7+
} from "@angular/core";
8+
import { NG_VALUE_ACCESSOR } from "@angular/forms";
9+
import { I18n } from "./../i18n/i18n.module";
10+
11+
@Component({
12+
selector: "ibm-selection-tile",
13+
template: `
14+
<label
15+
class="bx--tile bx--tile--selectable"
16+
tabindex="0"
17+
[for]="id"
18+
[ngClass]="{'bx--tile--is-selected' : selected}"
19+
[attr.aria-label]="i18n.get('TILES.TILE') | async">
20+
<input
21+
#input
22+
tabindex="-1"
23+
class="bx--tile-input"
24+
[id]="id"
25+
[type]="(multiple ? 'checkbox': 'radio')"
26+
[value]="value"
27+
[name]="name"
28+
(change)="onChange($event)"/>
29+
<div class="bx--tile__checkmark">
30+
<svg width="16" height="16" viewBox="0 0 16 16">
31+
<path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm3.646-10.854L6.75 10.043 4.354 7.646l-.708.708 3.104 3.103 5.604-5.603-.708-.708z"
32+
fill-rule="evenodd"/>
33+
</svg>
34+
</div>
35+
<div class="bx--tile-content">
36+
<ng-content></ng-content>
37+
</div>
38+
</label>
39+
`
40+
})
41+
export class SelectionTile {
42+
static tileCount = 0;
43+
/**
44+
* The unique id for the input.
45+
*/
46+
@Input() id = `tile-${SelectionTile.tileCount}`;
47+
/**
48+
* Updating the state of the input to match the state of the parameter passed in.
49+
* Set to `true` if this tile should be selected.
50+
*/
51+
@Input() set selected(value: boolean) {
52+
if (!this.input) { return; }
53+
this.input.nativeElement.checked = value ? true : null;
54+
}
55+
56+
get selected() {
57+
if (!this.input) { return; }
58+
return this.input.nativeElement.checked;
59+
}
60+
/**
61+
* The value for the tile. Returned via `ngModel` or `selected` event on the containing `TileGroup`.
62+
*/
63+
@Input() value: string;
64+
/**
65+
* Internal event used to notify the containing `TileGroup` of changes.
66+
*/
67+
@Output() change: EventEmitter<Event> = new EventEmitter();
68+
69+
/**
70+
* Set by the containing `TileGroup`. Used for the `name` property on the input.
71+
*/
72+
name: string;
73+
/**
74+
* Defines whether or not the `SelectionTile` supports selecting multiple tiles as opposed to single
75+
* tile selection.
76+
*/
77+
multiple: boolean;
78+
79+
@ViewChild("input") input;
80+
81+
constructor(public i18n: I18n) {
82+
SelectionTile.tileCount++;
83+
}
84+
85+
onChange(event) {
86+
this.change.emit(event);
87+
}
88+
}
89+
90+

src/tiles/tile-group.component.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import {
2+
Component,
3+
AfterContentInit,
4+
Input,
5+
Output,
6+
EventEmitter,
7+
HostBinding,
8+
ContentChildren,
9+
QueryList
10+
} from "@angular/core";
11+
import { SelectionTile } from "./selection-tile.component";
12+
import { NG_VALUE_ACCESSOR } from "@angular/forms";
13+
import { TileSelection } from "./tile-selection.interface";
14+
15+
@Component({
16+
selector: "ibm-tile-group",
17+
template: `<ng-content select="ibm-selection-tile"></ng-content>`,
18+
providers: [
19+
{
20+
provide: NG_VALUE_ACCESSOR,
21+
useExisting: TileGroup,
22+
multi: true
23+
}
24+
]
25+
})
26+
export class TileGroup implements AfterContentInit {
27+
static tileGroupCount = 0;
28+
/**
29+
* The tile group `name`
30+
*/
31+
@Input() name = `tile-group-${TileGroup.tileGroupCount}`;
32+
/**
33+
* Set to `true` to support multiple tile selection
34+
*/
35+
@Input() multiple = false;
36+
37+
/**
38+
* Emits an event when the tile selection changes.
39+
*
40+
* Emits an object that looks like:
41+
* ```javascript
42+
* {
43+
* value: "something",
44+
* selected: true,
45+
* name: "tile-group-1"
46+
* }
47+
* ```
48+
*/
49+
@Output() selected: EventEmitter<TileSelection> = new EventEmitter();
50+
51+
@HostBinding("class.bx--tile-group") tileGroupClass = true;
52+
53+
@ContentChildren(SelectionTile) selectionTiles: QueryList<SelectionTile>;
54+
55+
constructor() {
56+
TileGroup.tileGroupCount++;
57+
}
58+
59+
onChange = (_: any) => { };
60+
61+
onTouched = () => { };
62+
63+
ngAfterContentInit() {
64+
this.selectionTiles.forEach(tile => {
65+
tile.name = this.name;
66+
tile.change.subscribe(() => {
67+
this.selected.emit({
68+
value: tile.value,
69+
selected: tile.selected,
70+
name: this.name
71+
});
72+
this.onChange(tile.value);
73+
});
74+
tile.multiple = this.multiple;
75+
});
76+
}
77+
78+
writeValue(value: any) {
79+
if (!this.selectionTiles) { return; }
80+
this.selectionTiles.forEach(tile => {
81+
if (tile.value === value) {
82+
tile.selected = true;
83+
} else {
84+
tile.selected = false;
85+
}
86+
});
87+
}
88+
89+
registerOnChange(fn: any) {
90+
this.onChange = fn;
91+
}
92+
93+
registerOnTouched(fn: any) {
94+
this.onTouched = fn;
95+
}
96+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface TileSelection {
2+
value: string;
3+
selected: boolean;
4+
name: string;
5+
}

src/tiles/tiles.module.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,35 @@ import { CommonModule } from "@angular/common";
33

44
import { Tile } from "./tile.component";
55
import { ClickableTile } from "./clickable-tile.component";
6+
import { ExpandableTile } from "./expandable-tile.component";
7+
import { SelectionTile } from "./selection-tile.component";
8+
import { TileGroup } from "./tile-group.component";
9+
import { I18nModule } from "./../i18n/i18n.module";
610

711
export { Tile } from "./tile.component";
812
export { ClickableTile } from "./clickable-tile.component";
13+
export { ExpandableTile } from "./expandable-tile.component";
14+
export { SelectionTile } from "./selection-tile.component";
15+
export { TileGroup } from "./tile-group.component";
916

1017
@NgModule({
1118
declarations: [
1219
Tile,
13-
ClickableTile
20+
ClickableTile,
21+
ExpandableTile,
22+
SelectionTile,
23+
TileGroup
1424
],
1525
exports: [
1626
Tile,
17-
ClickableTile
27+
ClickableTile,
28+
ExpandableTile,
29+
SelectionTile,
30+
TileGroup
1831
],
1932
imports: [
20-
CommonModule
33+
CommonModule,
34+
I18nModule
2135
]
2236
})
2337
export class TilesModule {}

src/tiles/tiles.stories.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { storiesOf, moduleMetadata } from "@storybook/angular";
22
import { withKnobs } from "@storybook/addon-knobs/angular";
3+
import { action } from "@storybook/addon-actions";
34

45
import { TilesModule } from "../";
56

@@ -40,4 +41,36 @@ storiesOf("Tiles", module)
4041
Click the tile to open the Carbon Design System
4142
</ibm-clickable-tile>
4243
`
44+
}))
45+
.add("Selectable", () => ({
46+
template: `
47+
<ibm-tile-group (selected)="selected($event)">
48+
<ibm-selection-tile value="tile1" [selected]="true">Selectable Tile</ibm-selection-tile>
49+
<ibm-selection-tile value="tile2">Selectable Tile</ibm-selection-tile>
50+
<ibm-selection-tile value="tile3">Selectable Tile</ibm-selection-tile>
51+
</ibm-tile-group>
52+
`,
53+
props: {
54+
selected: action("tile selected")
55+
}
56+
}))
57+
.add("Multi-select", () => ({
58+
template: `
59+
<ibm-tile-group (selected)="selected($event)" [multiple]="true">
60+
<ibm-selection-tile value="tile1" [selected]="true">Selectable Tile</ibm-selection-tile>
61+
<ibm-selection-tile value="tile2">Selectable Tile</ibm-selection-tile>
62+
<ibm-selection-tile value="tile3">Selectable Tile</ibm-selection-tile>
63+
</ibm-tile-group>
64+
`,
65+
props: {
66+
selected: action("tile selected")
67+
}
68+
}))
69+
.add("Expandable", () => ({
70+
template: `
71+
<ibm-expandable-tile>
72+
<span class="bx--tile-content__above-the-fold" style="height: 200px">Above the fold content here</span>
73+
<span class="bx--tile-content__below-the-fold" style="height: 400px">Below the fold content here</span>
74+
</ibm-expandable-tile>
75+
`
4376
}));

0 commit comments

Comments
 (0)