Skip to content

Commit fe27e2b

Browse files
authored
Merge pull request #76 from edcarroll/develop
v0.6.5 into master
2 parents c338840 + cd92e8c commit fe27e2b

33 files changed

+309
-119
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ Now you're good to go!
6060

6161
## Dependencies
6262

63-
* [Angular 2](https://angular.io) (^4.0.0)
63+
* [Angular](https://angular.io) (^4.0.0)
6464
* [Semantic UI CSS](http://semantic-ui.com/) (jQuery is **not** required)
6565

6666
## Components

components/accordion/accordion-panel.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {Transition, TransitionDirection} from '../transition/transition';
88
exportAs: 'suiAccordionPanel',
99
template: `
1010
<!-- Title -->
11-
<div class="title" [class.active]="isOpen" (click)="toggleOpen($event)" >
11+
<div class="title" [class.active]="isOpen" (click)="toggle()" >
1212
<ng-content select="[title]"></ng-content>
1313
</div>
1414
<!-- Content -->
@@ -50,6 +50,9 @@ export class SuiAccordionPanel {
5050
}
5151

5252
public set isOpen(value:boolean) {
53+
// Convert to boolean (fixes false != undefined)
54+
value = !!value;
55+
5356
if (value != this.isOpen) {
5457
// Only update if the value has changed.
5558
this._isOpen = value;
@@ -85,9 +88,7 @@ export class SuiAccordionPanel {
8588
this.isOpenChange = new EventEmitter<boolean>(false);
8689
}
8790

88-
public toggleOpen(event:MouseEvent) {
89-
event.preventDefault();
90-
91+
public toggle() {
9192
if (!this.isDisabled) {
9293
this.isOpen = !this.isOpen;
9394
}

components/accordion/accordion.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ export class SuiAccordion implements AfterContentInit {
5151
}
5252

5353
ngAfterContentInit() {
54+
this.updatePanels();
55+
56+
// Reconnect panels after they have updated.
57+
this.panels.changes.subscribe(() => setTimeout(() => this.updatePanels()));
58+
}
59+
60+
public updatePanels() {
5461
this.panels.forEach(p => this._service.addPanel(p));
5562
}
5663
}

components/dimmer/dimmer.module.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
import {NgModule} from '@angular/core';
2-
import {SUI_DIMMER_DIRECTIVES} from "./dimmer";
2+
import {SuiDimmer} from './dimmer';
33
import {CommonModule} from "@angular/common";
4+
import {SuiTransitionModule} from '../transition/transition.module';
45

56
@NgModule({
6-
imports: [CommonModule],
7-
declarations: SUI_DIMMER_DIRECTIVES,
8-
exports: SUI_DIMMER_DIRECTIVES
7+
imports: [
8+
CommonModule,
9+
SuiTransitionModule
10+
],
11+
declarations: [
12+
SuiDimmer
13+
],
14+
exports: [
15+
SuiDimmer
16+
]
917
})
1018
export class SuiDimmerModule {}

components/dimmer/dimmer.ts

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import {Component, Input, Output, HostBinding, HostListener, EventEmitter} from '@angular/core';
1+
import {Component, Input, Output, HostBinding, HostListener, EventEmitter, Renderer, ElementRef, ChangeDetectorRef} from '@angular/core';
2+
import {SuiTransition, Transition, TransitionDirection} from '../transition/transition';
3+
import {TransitionController} from '../transition/transition-controller';
24

35
@Component({
46
selector: 'sui-dimmer',
@@ -12,34 +14,72 @@ import {Component, Input, Output, HostBinding, HostListener, EventEmitter} from
1214
`,
1315
styles: [`
1416
:host.dimmer {
15-
transition: visibility 0.3s, opacity 0.3s ease;
16-
display: block;
17-
visibility: hidden;
18-
}
19-
20-
:host.active {
21-
visibility: visible;
17+
transition: none;
2218
}
2319
`]
2420
})
25-
export class SuiDimmer {
21+
export class SuiDimmer extends SuiTransition {
2622
@HostBinding('class.ui')
27-
@HostBinding('class.dimmer') classes = true;
23+
@HostBinding('class.dimmer')
24+
private _dimmerClasses:boolean;
25+
26+
private _transitionController:TransitionController;
2827

29-
@Input() public isClickable:boolean = true;
28+
private _isDimmed:boolean;
3029

3130
@HostBinding('class.active')
32-
@Input() public isDimmed:boolean = false;
31+
@Input()
32+
public get isDimmed() {
33+
return this._isDimmed;
34+
}
35+
36+
public set isDimmed(dimmed:boolean) {
37+
dimmed = !!dimmed;
38+
39+
if (!this._transitionController) {
40+
// Initialise transition functionality when first setting dimmed, to ensure initial state doesn't transition.
41+
this._transitionController = new TransitionController(dimmed, "block");
42+
43+
this.setTransitionController(this._transitionController);
44+
}
45+
46+
if (this._isDimmed != dimmed) {
47+
this._isDimmed = dimmed;
48+
49+
if (this._transitionController.isVisible != dimmed) {
50+
this._transitionController.stopAll();
51+
this._transitionController.animate(new Transition("fade", 300, dimmed ? TransitionDirection.In : TransitionDirection.Out));
52+
}
53+
}
54+
}
55+
56+
@Output()
57+
public isDimmedChange:EventEmitter<boolean>;
58+
59+
@Input()
60+
public isClickable:boolean;
61+
62+
@Input()
63+
public transition:string;
3364

34-
@Output() public isDimmedChange:EventEmitter<boolean> = new EventEmitter<boolean>(false);
65+
@Input()
66+
public transitionDuration:number;
67+
68+
constructor(renderer:Renderer, element:ElementRef, changeDetector:ChangeDetectorRef) {
69+
super(renderer, element, changeDetector);
70+
71+
this._isDimmed = false;
72+
this.isDimmedChange = new EventEmitter<boolean>();
73+
this.isClickable = true;
74+
75+
this._dimmerClasses = true;
76+
}
3577

3678
@HostListener('click')
37-
private click() {
79+
private onClick() {
3880
if (this.isClickable) {
3981
this.isDimmed = false;
4082
this.isDimmedChange.emit(this.isDimmed);
4183
}
4284
}
4385
}
44-
45-
export const SUI_DIMMER_DIRECTIVES = [SuiDimmer];

components/dropdown/dropdown-menu.ts

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
import {Directive, HostBinding, ContentChild, forwardRef, Renderer, ElementRef, AfterContentInit, ContentChildren, QueryList, Input, HostListener} from '@angular/core';
1+
import {Directive, HostBinding, ContentChild, forwardRef, Renderer, ElementRef, AfterContentInit, ContentChildren, QueryList, Input, HostListener, ChangeDetectorRef} from '@angular/core';
22
import {SuiTransition, Transition} from '../transition/transition';
33
import {DropdownService, DropdownAutoCloseType} from './dropdown.service';
44
import {TransitionController} from '../transition/transition-controller';
5-
import {KeyCode} from '../util/util';
5+
import {KeyCode, AugmentedElement, HandledMouseEvent} from '../util/util';
66
// Polyfill for IE
77
import "element-closest";
88

9-
interface AugmentedElement extends Element {
10-
closest(selector:string):AugmentedElement;
11-
}
12-
139
@Directive({
1410
// We must attach to every '.item' as Angular doesn't support > selectors.
1511
selector: '.item'
@@ -122,8 +118,8 @@ export class SuiDropdownMenu extends SuiTransition implements AfterContentInit {
122118
@Input()
123119
public menuSelectedItemClass:string;
124120

125-
constructor(renderer:Renderer, public element:ElementRef) {
126-
super(renderer, element);
121+
constructor(renderer:Renderer, public element:ElementRef, changeDetector:ChangeDetectorRef) {
122+
super(renderer, element, changeDetector);
127123

128124
// Initialise transition functionality.
129125
this._transitionController = new TransitionController(false);
@@ -139,14 +135,16 @@ export class SuiDropdownMenu extends SuiTransition implements AfterContentInit {
139135
}
140136

141137
@HostListener("click", ["$event"])
142-
public onClick(e:MouseEvent) {
143-
e.stopPropagation();
144-
145-
if (this._service.autoCloseMode == DropdownAutoCloseType.ItemClick) {
146-
const target = e.target as AugmentedElement;
147-
if (this.element.nativeElement.contains(target.closest(".item")) && !/input|textarea/i.test(target.tagName)) {
148-
// Once an item is selected, we can close the entire dropdown.
149-
this._service.setOpenState(false, true);
138+
public onClick(e:HandledMouseEvent) {
139+
if (!e.eventHandled) {
140+
e.eventHandled = true;
141+
142+
if (this._service.autoCloseMode == DropdownAutoCloseType.ItemClick) {
143+
const target = e.target as AugmentedElement;
144+
if (this.element.nativeElement.contains(target.closest(".item")) && !/input|textarea/i.test(target.tagName)) {
145+
// Once an item is selected, we can close the entire dropdown.
146+
this._service.setOpenState(false, true);
147+
}
150148
}
151149
}
152150
}
@@ -157,22 +155,13 @@ export class SuiDropdownMenu extends SuiTransition implements AfterContentInit {
157155
this._isOpenOnMousedown = this._service.isOpen;
158156
}
159157

160-
@HostListener("document:click", ["$event"])
161-
public onDocumentClick(e:MouseEvent) {
162-
if (this._isOpenOnMousedown) {
163-
if (this._service.autoCloseMode == DropdownAutoCloseType.ItemClick || this._service.autoCloseMode == DropdownAutoCloseType.OutsideClick) {
164-
// No need to reflect in parent since they are also bound to document.
165-
this._service.setOpenState(false);
166-
}
167-
}
168-
}
169-
170158
@HostListener("document:keydown", ["$event"])
171159
public onDocumentKeydown(e:KeyboardEvent) {
172160
// Only the root dropdown (i.e. not nested dropdowns) is responsible for keeping track of the currently selected item.
173161
if (this._service.isOpen && !this._service.isNested) {
174162
// Stop document events like scrolling while open.
175-
if ((e.target as Element).tagName != "INPUT" || e.keyCode == KeyCode.Enter) {
163+
const target = e.target as Element;
164+
if (!/input/i.test(target.tagName) || e.keyCode == KeyCode.Enter) {
176165
e.preventDefault();
177166
}
178167

components/dropdown/dropdown.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ export class DropdownService {
2929
return !!this.parent;
3030
}
3131

32-
constructor() {
32+
constructor(autoCloseMode:DropdownAutoCloseType = DropdownAutoCloseType.ItemClick) {
3333
this.isOpen = false;
3434
this.isOpenChange = new EventEmitter<boolean>();
3535

3636
this.isDisabled = false;
3737

38-
this.autoCloseMode = DropdownAutoCloseType.ItemClick;
38+
this.autoCloseMode = autoCloseMode;
3939

4040
this.children = [];
4141
}

components/dropdown/dropdown.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {TransitionController} from '../transition/transition-controller';
44
import {DropdownService, DropdownAutoCloseType} from './dropdown.service';
55
import {SuiDropdownMenu} from './dropdown-menu';
66
import {PositioningService, PositioningPlacement} from '../util/positioning.service';
7-
import {KeyCode} from '../util/util';
7+
import {KeyCode, HandledMouseEvent, HandledKeyboardEvent} from '../util/util';
88

99
@Directive({
1010
selector: '[suiDropdown]'
@@ -68,7 +68,7 @@ export class SuiDropdown implements AfterContentInit {
6868
this.service.autoCloseMode = value;
6969
}
7070

71-
constructor() {
71+
constructor(private _element:ElementRef) {
7272
this.service = new DropdownService();
7373
}
7474

@@ -91,20 +91,34 @@ export class SuiDropdown implements AfterContentInit {
9191
}
9292

9393
@HostListener("click", ['$event'])
94-
public onClick(e:MouseEvent) {
95-
// Block the click event from being fired on parent dropdowns.
96-
e.stopPropagation();
94+
public onClick(e:HandledMouseEvent) {
95+
if (!e.eventHandled) {
96+
e.eventHandled = true;
9797

98-
this.service.toggleOpenState();
98+
this.service.toggleOpenState();
99+
}
99100
}
100101

101102
@HostListener("keypress", ['$event'])
102-
public onKeypress(e:KeyboardEvent) {
103+
public onKeypress(e:HandledKeyboardEvent) {
103104
// Block the keyboard event from being fired on parent dropdowns.
104-
if (e.keyCode == KeyCode.Enter) {
105-
e.stopPropagation();
105+
if (!e.eventHandled) {
106+
107+
if (e.keyCode == KeyCode.Enter) {
108+
e.eventHandled = true;
109+
110+
this.service.setOpenState(true);
111+
}
112+
}
113+
}
106114

107-
this.service.setOpenState(true);
115+
@HostListener("document:click", ["$event"])
116+
public onDocumentClick(e:MouseEvent) {
117+
if (!this._element.nativeElement.contains(e.target)) {
118+
if (this.service.autoCloseMode == DropdownAutoCloseType.ItemClick || this.service.autoCloseMode == DropdownAutoCloseType.OutsideClick) {
119+
// No need to reflect in parent since they are also bound to document.
120+
this.service.setOpenState(false);
121+
}
108122
}
109123
}
110124
}

components/popup/popup.directive.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import {PositioningPlacement} from '../util/positioning.service';
44

55
export type PopupTrigger = "hover" | "click" | "outsideClick" | "focus" | "manual";
66

7+
export interface IPopup {
8+
open():void;
9+
close():void;
10+
toggle():void;
11+
}
12+
713
// Creates essentially a 'string' enum.
814
export const PopupTrigger = {
915
Hover: "hover" as PopupTrigger,
@@ -17,7 +23,7 @@ export const PopupTrigger = {
1723
selector: '[suiPopup]',
1824
exportAs: 'suiPopup'
1925
})
20-
export class SuiPopupDirective {
26+
export class SuiPopupDirective implements IPopup {
2127
private _config:IPopupConfiguration;
2228
private _placement:PositioningPlacement;
2329

@@ -168,16 +174,18 @@ export class SuiPopupDirective {
168174
@HostListener("click")
169175
private onClick() {
170176
if (this.popupTrigger == PopupTrigger.Click || this.popupTrigger == PopupTrigger.OutsideClick) {
171-
event.stopPropagation();
172-
173177
this.toggle();
174178
}
175179
}
176180

177181
@HostListener("document:click", ["$event"])
178182
public onDocumentClick(e:MouseEvent) {
179183
if (this._popupComponentRef && this.popupTrigger == PopupTrigger.OutsideClick) {
180-
this.close();
184+
const target = e.target as Element;
185+
// Replacement for e.stopPropagation();
186+
if (!(this._element.nativeElement as Element).contains(target)) {
187+
this.close();
188+
}
181189
}
182190
}
183191

components/popup/popup.module.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {NgModule} from '@angular/core';
22
import {CommonModule} from "@angular/common";
33
import {SuiTransitionModule} from "../transition/transition.module";
4-
import {SuiPopupDirective} from './popup.directive';
4+
import {SuiPopupDirective, IPopup} from './popup.directive';
55
import {SuiPopup} from './popup';
66
import {SuiPopupArrow} from './popup-arrow';
77

@@ -23,4 +23,7 @@ import {SuiPopupArrow} from './popup-arrow';
2323
SuiPopup
2424
]
2525
})
26-
export class SuiPopupModule {}
26+
27+
export class SuiPopupModule {}
28+
29+
export {IPopup};

0 commit comments

Comments
 (0)