Skip to content
This repository was archived by the owner on May 20, 2023. It is now read-only.

Commit d48c103

Browse files
authored
Add Material List (#49)
* Material Expansionpanel: Add `autoDismissable` directive. * Material Progress: Handle changes to "indeterminate" state. * Scorecard: Add input to display vertically. * Update styles to meet Material UI spec. * Add qualifying remark about alpha status
1 parent 25f22fa commit d48c103

19 files changed

+478
-20
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
## 0.3.1-alpha
2+
3+
This code is considered production quality, but depends on angular2: 3.0.0-alpha.
4+
The alpha tag represents the evolving nature of the AngularDart api, not
5+
code quality (3.0.0-alpha is used in production Google apps).
6+
7+
* Add Material List.
8+
* Material Expansionpanel: Add `autoDismissable` directive.
9+
* Material Progress: Handle changes to "indeterminate" state.
10+
* Scorecard: Add input to display vertically.
11+
* Update styles to meet Material UI spec.
12+
113
## 0.3.0-alpha
214

315
This code is considered production quality, but depends on angular2: 3.0.0-alpha.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ possible.
3131
*`<material-dialog>`
3232
*`<material-popup>`
3333
*`<material-tooltip>`
34-
* `<material-list>`
34+
* `<material-list>`
3535
*`<material-select>`
36+
*`<material-tree>`
3637
*`<material-auto-suggest-input>`
3738
*`<material-date-range-picker>`
3839
*`<material-menu>`

lib/angular2_components.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const List<Type> materialDirectives = const [
3535
MaterialInkTooltipComponent,
3636
MaterialInputComponent,
3737
MaterialInputDefaultValueAccessor,
38+
MaterialListComponent,
39+
MaterialListItemComponent,
3840
MaterialMultilineInputComponent,
3941
MaterialPaperTooltipComponent,
4042
MaterialPopupComponent,

lib/src/all_components.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ export 'components/material_input/material_input.dart'
3333
hide materialInputErrorKey;
3434
export 'components/material_input/material_input_default_value_accessor.dart';
3535
export 'components/material_input/material_input_multiline.dart';
36+
export 'components/material_list/material_list.dart';
37+
export 'components/material_list/material_list_item.dart';
38+
export 'components/material_list/material_list_size.dart';
3639
export 'components/material_popup/material_popup.dart';
3740
export 'components/material_progress/material_progress.dart';
3841
export 'components/material_radio/material_radio.dart';
@@ -59,3 +62,4 @@ export 'laminate/components/modal/modal.dart';
5962
export 'laminate/overlay/module.dart';
6063
export 'laminate/popup/module.dart';
6164
export 'laminate/popup/popup.dart';
65+
export 'model/selection/selection_model.dart';
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:html';
7+
8+
import 'package:angular2/angular2.dart';
9+
10+
import '../material_expansionpanel.dart';
11+
import '../../../laminate/overlay/module.dart'
12+
show overlayContainerToken;
13+
14+
/// A directive that automatically collapses [MaterialExpansionPanel].
15+
///
16+
/// When a [MaterialExpansionPanel] is expanded, any click outside of it will
17+
/// automatically collapses the panel.
18+
///
19+
/// __Example Usage:__
20+
///
21+
/// <material-expansionpanel autoDismissable>
22+
/// </material-expansionpanel>
23+
///
24+
@Directive(
25+
selector: 'material-expansionpanel[autoDismissable]',
26+
host: const {'(expandedChange)': r'onExpandedChanged($event)'})
27+
class MaterialExpansionPanelAutoDismiss implements OnDestroy {
28+
final MaterialExpansionPanel _expansionPanel;
29+
30+
/// The root node that all spawned elements will belong to.
31+
///
32+
/// E.g. modal, dialog, popups.
33+
final Element _overlayContainerToken;
34+
final ElementRef _element;
35+
36+
StreamController<Event> _clicksOutsideController;
37+
StreamSubscription<Event> _clicksOutsideSubscription;
38+
StreamSubscription<MouseEvent> _mouseUpListener;
39+
40+
MaterialExpansionPanelAutoDismiss(
41+
this._expansionPanel,
42+
@Optional() @Inject(overlayContainerToken) this._overlayContainerToken,
43+
this._element) {
44+
_clicksOutsideController = new StreamController.broadcast(
45+
sync: true,
46+
onListen: () {
47+
_mouseUpListener = document.onMouseUp.listen(_onMouseUp);
48+
},
49+
onCancel: () {
50+
_mouseUpListener.cancel();
51+
_mouseUpListener = null;
52+
});
53+
}
54+
55+
/// Handles expanded status changes from the panel.
56+
void onExpandedChanged(bool expand) {
57+
_clicksOutsideSubscription?.cancel();
58+
if (expand) {
59+
_clicksOutsideSubscription = _clicksOutsideController.stream
60+
.listen((e) => _expansionPanel.collapse(byUserAction: false));
61+
}
62+
}
63+
64+
@override
65+
void ngOnDestroy() {
66+
_clicksOutsideSubscription?.cancel();
67+
}
68+
69+
void _onMouseUp(MouseEvent e) {
70+
var node = e.target as Element;
71+
while (node != null) {
72+
var tagName = node.tagName.toLowerCase();
73+
if (node == this._element.nativeElement) {
74+
// Excludes elements belonging to this panel.
75+
return;
76+
} else if (node == _overlayContainerToken) {
77+
// Excludes elements spawned by portals.
78+
return;
79+
} else if (tagName == 'body') {
80+
// Fires for these elements that can reach the 'body'.
81+
//
82+
// Choosing 'body' but not 'html' is because we want to exclude
83+
// browser scroll-bars which are direct children of 'html'.
84+
_clicksOutsideController.add(e);
85+
return;
86+
} else if (tagName == 'material-button' ||
87+
tagName == 'dropdown-button' ||
88+
tagName == 'input' ||
89+
tagName == 'a') {
90+
// Excludes any clickable elements.
91+
return;
92+
}
93+
node = node.parent;
94+
}
95+
// Treats clicks on dangling elements as inside the panel.
96+
//
97+
// E.g. clicks on the header of [MaterialExpansionPanel] with
98+
// 'hideExpandedHeader' set to true would result in the header being
99+
// removed from DOM immediately.
100+
}
101+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:angular2/angular2.dart';
6+
7+
import '../../model/ui/accepts_width.dart';
8+
import '../../utils/angular/properties/properties.dart';
9+
import 'material_list_size.dart';
10+
export 'material_list_size.dart';
11+
12+
/// Material List is a container component for a set of items with which the
13+
/// user is intended to interact.
14+
///
15+
/// It forms the basis for selection and menu components.
16+
/// The `MaterialListComponent` class acts as the root node for a list
17+
/// providing styling and the ability to collect item events.
18+
///
19+
///
20+
/// __Example usage:__
21+
/// <material-list size="medium">
22+
/// <material-list-item>Item 1</material-list-item>
23+
/// <material-list-item>Item 2/material-list-item>
24+
/// </material-list>
25+
///
26+
/// ## List Item Grouping
27+
///
28+
/// Wrap `material-list-item` elements in a container with attribute
29+
/// `group` for a divider line between other groupings.
30+
///
31+
/// <material-list>
32+
/// <div group>
33+
/// <material-list-item>Item</material-list-item>
34+
/// <material-list-item>Item</material-list-item>
35+
/// <material-list-item>Item</material-list-item>
36+
/// </div>
37+
/// <material-list-item>Item</material-list-item>
38+
/// <material-list-item>Item</material-list-item>
39+
/// <div group>
40+
/// <material-list-item>Item</material-list-item>
41+
/// <material-list-item>Item</material-list-item>
42+
/// <material-list-item>Item</material-list-item>
43+
/// </div>
44+
/// <material-list>
45+
///
46+
/// ## List Item Group Labels
47+
///
48+
/// Denote the label for a list item group using the `label` attribute.
49+
///
50+
/// <material-list>
51+
/// <div group>
52+
/// <div label>My Menu Group</div>
53+
/// <material-list-item>Item</material-list-item>
54+
/// <material-list-item>Item</material-list-item>
55+
/// <material-list-item>Item</material-list-item>
56+
/// </div>
57+
/// </material-list>
58+
///
59+
/// __Attributes:__
60+
///
61+
/// - `size: string {x-small, small, medium, large, x-large}` Sizes for the
62+
/// list, resulting in the width being 64px * {1.5, 3, 5, 6, 7} respectively.
63+
/// The default size is "auto" which sizes the list to its contents.
64+
/// - `min-size: string {x-small, small, medium, large, x-large}` Minimum size
65+
/// for the list, resulting in the width at least the specified width.
66+
@Component(
67+
selector: 'material-list',
68+
styleUrls: const ['material_list.scss.css'],
69+
directives: const [NgClass],
70+
host: const {'[attr.size]': 'size'},
71+
template: '<ng-content></ng-content>',
72+
changeDetection: ChangeDetectionStrategy.OnPush)
73+
class MaterialListComponent implements AcceptsWidth {
74+
@Input()
75+
String size = MaterialListSize.auto;
76+
77+
/// Preset width, 1 through 5. By default, the material list will expand to
78+
/// the full width of its parent. Note: The spec clearly lays out predefined
79+
/// list sizes so use the default, expanding size, sparingly.
80+
/// Each width multiplies the base block width (64px on desktop and tablet) by
81+
/// [1.5, 3, 5, 6, 7], respectively to obtain a predictable width.
82+
/// Set to 0 to have the list expand to the full width of its parent.
83+
@Deprecated('Use size attribute instead.')
84+
@Input()
85+
set width(val) {
86+
val = getInt(val);
87+
if (val >= 0 && val < MaterialListSize.values.length) {
88+
size = MaterialListSize.values[val];
89+
}
90+
}
91+
}

lib/src/components/material_list/material_list.scss.css

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:html' as dom;
6+
7+
import 'package:angular2/angular2.dart';
8+
9+
import '../button_decorator/button_decorator.dart';
10+
import '../mixins/has_tab_index.dart';
11+
import '../mixins/material_dropdown_base.dart';
12+
import '../../model/a11y/active_item_mixin.dart';
13+
import '../../utils/angular/properties/properties.dart';
14+
import '../../utils/browser/dom_service/dom_service.dart';
15+
import '../../utils/disposer/disposer.dart';
16+
17+
/// Material List Item is a block element intended for user interaction; it has
18+
/// `:hover` styling and emits and `trigger` event when the user clicks or
19+
/// presses `enter` or `space` keys.
20+
///
21+
/// See [MaterialListComponent] for item **grouping** and **labelling**.
22+
///
23+
/// Note: If the material-list-item has a [DropdownHandle] in its ancestry, the
24+
/// dropdown will be closed on triggering (i.e. clicking or pressing enter/space
25+
/// on) the list item if [closeOnActivate] is true.
26+
///
27+
/// __Example usage:__
28+
///
29+
/// <material-list>
30+
/// <material-list-item (trigger)="select(1)">Item 1</material-list-item>
31+
/// <material-list-item (trigger)="select(2)">Item 2/material-list-item>
32+
/// </material-list>
33+
///
34+
/// __Properties:__
35+
///
36+
/// - `disabled:bool` -- disables the trigger and gives item a disabled style.
37+
/// - `active:bool` -- marks item as active from keyboard selection.
38+
/// - `closeOnActivate:bool` -- causes dropdown to be closed on activation.
39+
/// True by default.
40+
///
41+
/// __Events:__
42+
///
43+
/// - `trigger:MouseEvent|KeyboardEvent` -- fired when either mouse is clicked
44+
/// or __enter__ or __space__ keys are pressed.
45+
///
46+
// TODO(google): should activate/deactive on mouse hover
47+
@Component(
48+
selector: 'material-list-item',
49+
inputs: const [
50+
'disabled',
51+
'active', // from ActiveItemMixin
52+
],
53+
outputs: const ['trigger'],
54+
host: const {
55+
'class': 'item',
56+
'[class.active]': 'active',
57+
'[class.disabled]': 'disabled',
58+
'(click)': r'handleClick($event)',
59+
'(keypress)': r'handleKeyPress($event)',
60+
'[attr.aria-disabled]': 'disabledStr',
61+
'[attr.tabindex]': 'tabIndex',
62+
'(mouseenter)': 'onMouseEnter()',
63+
'(mouseleave)': 'onMouseLeave()',
64+
'[attr.role]': 'role != null ? role : "button"'
65+
},
66+
styleUrls: const ['material_list_item.scss.css'],
67+
template: '<ng-content></ng-content>',
68+
changeDetection: ChangeDetectionStrategy.OnPush)
69+
class MaterialListItemComponent extends ButtonDirective
70+
with HasTabIndex, ActiveItemMixin
71+
implements OnDestroy {
72+
final _disposer = new Disposer.oneShot();
73+
final DropdownHandle _dropdown;
74+
final String role;
75+
76+
final String _hostTabIndex;
77+
78+
@override
79+
String get hostTabIndex => _hostTabIndex;
80+
81+
@override
82+
dom.HtmlElement element;
83+
84+
@override
85+
DomService domService;
86+
87+
MaterialListItemComponent(
88+
ElementRef elementRef,
89+
this.domService,
90+
@Optional() this._dropdown,
91+
@Attribute('tabindex') this._hostTabIndex,
92+
@Attribute('role') this.role)
93+
: super(elementRef) {
94+
if (_dropdown != null) {
95+
_disposer.addDisposable(trigger.listen(handleActivate));
96+
}
97+
this.element = elementRef.nativeElement;
98+
}
99+
100+
bool _closeOnActivate = true;
101+
102+
/// Whether the encompassing dropdown should be close on selecting
103+
/// this item.
104+
bool get closeOnActivate => _closeOnActivate;
105+
@Input()
106+
set closeOnActivate(value) {
107+
_closeOnActivate = getBool(value);
108+
}
109+
110+
@override
111+
void ngOnDestroy() {
112+
_disposer.dispose();
113+
}
114+
115+
void handleActivate(dom.UIEvent _) {
116+
if (closeOnActivate) _dropdown?.close();
117+
}
118+
}

lib/src/components/material_list/material_list_item.scss.css

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// Size names (and corresponding values) for the standard widths of
6+
/// `material-list`.
7+
class MaterialListSize {
8+
static const String auto = 'auto';
9+
static const String xSmall = 'x-small';
10+
static const String small = 'small';
11+
static const String medium = 'medium';
12+
static const String large = 'large';
13+
static const String xLarge = 'x-large';
14+
static List<String> get values =>
15+
const <String>[auto, xSmall, small, medium, large, xLarge];
16+
}

0 commit comments

Comments
 (0)