Skip to content

Commit

Permalink
feat(list): added hot-keys for multiselect
Browse files Browse the repository at this point in the history
  • Loading branch information
lkramarov committed Jun 7, 2018
1 parent 58ac0fa commit 1b77d4f
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 152 deletions.
11 changes: 2 additions & 9 deletions src/cdk/a11y/key-manager/focus-key-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,10 @@ export class FocusKeyManager<T> extends ListKeyManager<IFocusableOption & T> {
}

/**
* Sets the active item to the item at the specified
* index and focuses the newly active item.
* @param index Index of the item to be set as active.
*/
setActiveItem(index: number): void;

/**
* Sets the active item to the item that is specified and focuses it.
* Sets the active item or index to the item that is specified and focuses it.
* @param item Item to be set as active.
*/
setActiveItem(item: T): void;
setActiveItem(item: number | T): void;

setActiveItem(item: any): void {
super.setActiveItem(item);
Expand Down
65 changes: 26 additions & 39 deletions src/cdk/a11y/key-manager/list-key-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
/** Stream that emits whenever the active item of the list manager changes. */
change = new Subject<number>();

previousActiveItemIndex = -1;
private _activeItemIndex = -1;
private _activeItem: T;
private _wrap: boolean = false;
Expand All @@ -48,6 +49,7 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
private _vertical = true;
private _horizontal: 'ltr' | 'rtl' | null;

private _scrollSize: number = 0;
/**
* Predicate function that can be used to check whether an item should be skipped
* by the key manager. By default, disabled items are skipped.
Expand All @@ -72,10 +74,17 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
}
}

withScrollSize(scrollSize: number): this {
this._scrollSize = scrollSize;

return this;
}

/**
* Turns on wrapping mode, which ensures that the active item will wrap to
* the other end of list when there are no more items in the given direction.
*/

withWrap(): this {
this._wrap = true;

Expand Down Expand Up @@ -144,27 +153,21 @@ export class ListKeyManager<T extends IListKeyManagerOption> {

/**
* Sets the active item to the item at the index specified.
* @param index The index of the item to be set as active.
* @param index The index of the item to be set as active or item The item to be set as active.
*/
setActiveItem(index: number): void;

/**
* Sets the active item to the specified item.
* @param item The item to be set as active.
*/
setActiveItem(item: T): void;
setActiveItem(index: number | T): void;

/**
* Sets the active item to the item at the index specified.
* @param index The index of the item to be set as active.
*/
setActiveItem(index: any): void {
const previousIndex = this._activeItemIndex;
this.previousActiveItemIndex = this._activeItemIndex;

this._activeItemIndex = index;
this._activeItem = this._items.toArray()[index];

if (this._activeItemIndex !== previousIndex) {
if (this._activeItemIndex !== this.previousActiveItemIndex) {
this.change.next(index);
}
}
Expand Down Expand Up @@ -239,7 +242,7 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
}

// Index of the currently active item.
get activeItemIndex(): number | null {
get activeItemIndex(): number {
return this._activeItemIndex;
}

Expand Down Expand Up @@ -269,7 +272,7 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
: this._setActiveItemByDelta(-1);
}

setNextPageItemActive(delta: number): void {
setNextPageItemActive(delta: number = this._scrollSize): void {
const nextItemIndex = this._activeItemIndex + delta;

if (nextItemIndex >= this._items.length) {
Expand All @@ -279,7 +282,7 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
}
}

setPreviousPageItemActive(delta: number): void {
setPreviousPageItemActive(delta: number = this._scrollSize): void {
const nextItemIndex = this._activeItemIndex - delta;

if (nextItemIndex <= 0) {
Expand All @@ -289,40 +292,28 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
}
}

/**
* Allows setting the active without any other effects.
* @param index Index of the item to be set as active.
*/
updateActiveItem(index: number): void;

/**
* Allows setting the active item without any other effects.
* @param item Item to be set as active.
* @param item Item to be set as active or index Index of the item to be set as active..
*/
updateActiveItem(item: T): void;
updateActiveItem(item: number | T): void;

updateActiveItem(item: any): void {
const itemArray = this._getItemsArray();
const index = typeof item === 'number' ? item : itemArray.indexOf(item);

this.previousActiveItemIndex = this._activeItemIndex;

this._activeItemIndex = index;
this._activeItem = itemArray[index];
}

/**
* Allows setting of the activeItemIndex without any other effects.
* @param index The new activeItemIndex.
*/
updateActiveItemIndex(index: number) {
this.updateActiveItem(index);
}

/**
* This method sets the active item, given a list of items and the delta between the
* currently active item and the new active item. It will calculate differently
* depending on whether wrap mode is turned on.
*/
private _setActiveItemByDelta(delta: -1 | 1): void {
private _setActiveItemByDelta(delta: number): void {
this._wrap ? this._setActiveInWrapMode(delta) : this._setActiveInDefaultMode(delta);
}

Expand All @@ -331,7 +322,7 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
* down the list until it finds an item that is not disabled, and it will wrap if it
* encounters either end of the list.
*/
private _setActiveInWrapMode(delta: -1 | 1): void {
private _setActiveInWrapMode(delta: number): void {
const items = this._getItemsArray();

for (let i = 1; i <= items.length; i++) {
Expand All @@ -351,7 +342,7 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
* continue to move down the list until it finds an item that is not disabled. If
* it encounters either end of the list, it will stop and not wrap.
*/
private _setActiveInDefaultMode(delta: -1 | 1): void {
private _setActiveInDefaultMode(delta: number): void {
this._setActiveItemByIndex(this._activeItemIndex + delta, delta);
}

Expand All @@ -360,19 +351,15 @@ export class ListKeyManager<T extends IListKeyManagerOption> {
* item is disabled, it will move in the fallbackDelta direction until it either
* finds an enabled item or encounters the end of the list.
*/
private _setActiveItemByIndex(index: number, fallbackDelta: -1 | 1): void {
private _setActiveItemByIndex(index: number, fallbackDelta: number): void {
const items = this._getItemsArray();

if (!items[index]) {
return;
}
if (!items[index]) { return; }

while (this._skipPredicateFn(items[index])) {
index += fallbackDelta;

if (!items[index]) {
return;
}
if (!items[index]) { return; }
}

this.setActiveItem(index);
Expand Down
4 changes: 2 additions & 2 deletions src/cdk/collections/selection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,11 @@ describe('SelectionModel', () => {
it('should be able to determine whether it has a value', () => {
const model = new SelectionModel();

expect(model.hasValue()).toBe(false);
expect(model.isEmpty()).toBe(true);

model.select(1);

expect(model.hasValue()).toBe(true);
expect(model.isEmpty()).toBe(false);
});

it('should be able to toggle an option', () => {
Expand Down
27 changes: 12 additions & 15 deletions src/cdk/collections/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ export class SelectionModel<T> {
/** Cache for the array value of the selected items. */
private _selected: T[] | null;

constructor(private _multiple = false, initiallySelectedValues?: T[], private _emitChanges = true) {
constructor(
private _multiple = false,
initiallySelectedValues?: T[],
private _emitChanges = true
) {
if (initiallySelectedValues && initiallySelectedValues.length) {
if (_multiple) {
initiallySelectedValues.forEach((value) => this._markSelected(value));
Expand Down Expand Up @@ -58,7 +62,9 @@ export class SelectionModel<T> {
*/
deselect(...values: T[]): void {
this._verifyValueAssignment(values);

values.forEach((value) => this._unmarkSelected(value));

this._emitChangeEvent();
}

Expand Down Expand Up @@ -91,13 +97,6 @@ export class SelectionModel<T> {
return this._selection.size === 0;
}

/**
* Determines whether the model has a value.
*/
hasValue(): boolean {
return !this.isEmpty();
}

/**
* Sorts the selected values based on a predicate function.
*/
Expand All @@ -106,7 +105,7 @@ export class SelectionModel<T> {
}

/** Emits a change event and clears the records of selected and deselected values. */
private _emitChangeEvent() {
private _emitChangeEvent(): void {
// Clear the selected values so they can be re-cached.
this._selected = null;

Expand All @@ -121,11 +120,9 @@ export class SelectionModel<T> {
}

/** Selects a value. */
private _markSelected(value: T) {
private _markSelected(value: T): void {
if (!this.isSelected(value)) {
if (!this._multiple) {
this._unmarkAll();
}
if (!this._multiple) { this._unmarkAll(); }

this._selection.add(value);

Expand All @@ -136,7 +133,7 @@ export class SelectionModel<T> {
}

/** Deselects a value. */
private _unmarkSelected(value: T) {
private _unmarkSelected(value: T): void {
if (this.isSelected(value)) {
this._selection.delete(value);

Expand All @@ -147,7 +144,7 @@ export class SelectionModel<T> {
}

/** Clears out the selected values. */
private _unmarkAll() {
private _unmarkAll(): void {
if (!this.isEmpty()) {
this._selection.forEach((value) => this._unmarkSelected(value));
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib-dev/list/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class DemoComponent {
];

onSelectionChange($event: McListSelectionChange) {
// console.log('onSelectionChange');
console.log(`onSelectionChange: ${$event.option.value}`);
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/lib-dev/list/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
<!--<br><br><br>-->

<h5>multiple selection</h5>
<mc-list-selection [(ngModel)]="multipleSelected" (selectionChange)="onSelectionChange($event)">
<mc-list-selection
class="mc-no-select"
[(ngModel)]="multipleSelected"
(selectionChange)="onSelectionChange($event)">
<mc-list-option value="Disabled" disabled>Disabled</mc-list-option>
<mc-list-option value="Normal">Normal</mc-list-option>
<mc-list-option value="Hovered" class="mc-hovered">Hovered</mc-list-option>
Expand Down
8 changes: 8 additions & 0 deletions src/lib/core/styles/common/_list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,11 @@
display: none;
}
}

.mc-no-select {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
}
Loading

0 comments on commit 1b77d4f

Please sign in to comment.