UNPKG

ipsos-components

Version:

Material Design components for Angular

195 lines (162 loc) 5.17 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {Subject} from 'rxjs/Subject'; /** * Class to be used to power selecting one or more options from a list. */ export class SelectionModel<T> { /** Currently-selected values. */ private _selection: Set<T> = new Set(); /** Keeps track of the deselected options that haven't been emitted by the change event. */ private _deselectedToEmit: T[] = []; /** Keeps track of the selected option that haven't been emitted by the change event. */ private _selectedToEmit: T[] = []; /** Cache for the array value of the selected items. */ private _selected: T[] | null; /** Selected value(s). */ get selected(): T[] { if (!this._selected) { this._selected = Array.from(this._selection.values()); } return this._selected; } /** Event emitted when the value has changed. */ onChange: Subject<SelectionChange<T>> | null = this._emitChanges ? new Subject() : null; constructor( private _isMulti = false, initiallySelectedValues?: T[], private _emitChanges = true) { if (initiallySelectedValues) { if (_isMulti) { initiallySelectedValues.forEach(value => this._markSelected(value)); } else { this._markSelected(initiallySelectedValues[0]); } // Clear the array in order to avoid firing the change event for preselected values. this._selectedToEmit.length = 0; } } /** * Selects a value or an array of values. */ select(...values: T[]): void { this._verifyValueAssignment(values); values.forEach(value => this._markSelected(value)); this._emitChangeEvent(); } /** * Deselects a value or an array of values. */ deselect(...values: T[]): void { this._verifyValueAssignment(values); values.forEach(value => this._unmarkSelected(value)); this._emitChangeEvent(); } /** * Toggles a value between selected and deselected. */ toggle(value: T): void { this.isSelected(value) ? this.deselect(value) : this.select(value); } /** * Clears all of the selected values. */ clear(): void { this._unmarkAll(); this._emitChangeEvent(); } /** * Determines whether a value is selected. */ isSelected(value: T): boolean { return this._selection.has(value); } /** * Determines whether the model does not have a value. */ isEmpty(): boolean { 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. */ sort(predicate?: (a: T, b: T) => number): void { if (this._isMulti && this._selected) { this._selected.sort(predicate); } } /** Emits a change event and clears the records of selected and deselected values. */ private _emitChangeEvent() { // Clear the selected values so they can be re-cached. this._selected = null; if (this._selectedToEmit.length || this._deselectedToEmit.length) { const eventData = new SelectionChange(this._selectedToEmit, this._deselectedToEmit); if (this.onChange) { this.onChange.next(eventData); } this._deselectedToEmit = []; this._selectedToEmit = []; } } /** Selects a value. */ private _markSelected(value: T) { if (!this.isSelected(value)) { if (!this._isMulti) { this._unmarkAll(); } this._selection.add(value); if (this._emitChanges) { this._selectedToEmit.push(value); } } } /** Deselects a value. */ private _unmarkSelected(value: T) { if (this.isSelected(value)) { this._selection.delete(value); if (this._emitChanges) { this._deselectedToEmit.push(value); } } } /** Clears out the selected values. */ private _unmarkAll() { if (!this.isEmpty()) { this._selection.forEach(value => this._unmarkSelected(value)); } } /** * Verifies the value assignment and throws an error if the specified value array is * including multiple values while the selection model is not supporting multiple values. */ private _verifyValueAssignment(values: T[]) { if (values.length > 1 && !this._isMulti) { throw getMultipleValuesInSingleSelectionError(); } } } /** * Describes an event emitted when the value of a MatSelectionModel has changed. * @docs-private */ export class SelectionChange<T> { constructor(public added?: T[], public removed?: T[]) { } } /** * Returns an error that reports that multiple values are passed into a selection model * with a single value. */ export function getMultipleValuesInSingleSelectionError() { return Error('Cannot pass multiple values into SelectionModel with single-value mode.'); }