selection-model-CeeHVIcP.mjs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. import { Subject } from 'rxjs';
  2. /**
  3. * Class to be used to power selecting one or more options from a list.
  4. */
  5. class SelectionModel {
  6. _multiple;
  7. _emitChanges;
  8. compareWith;
  9. /** Currently-selected values. */
  10. _selection = new Set();
  11. /** Keeps track of the deselected options that haven't been emitted by the change event. */
  12. _deselectedToEmit = [];
  13. /** Keeps track of the selected options that haven't been emitted by the change event. */
  14. _selectedToEmit = [];
  15. /** Cache for the array value of the selected items. */
  16. _selected;
  17. /** Selected values. */
  18. get selected() {
  19. if (!this._selected) {
  20. this._selected = Array.from(this._selection.values());
  21. }
  22. return this._selected;
  23. }
  24. /** Event emitted when the value has changed. */
  25. changed = new Subject();
  26. constructor(_multiple = false, initiallySelectedValues, _emitChanges = true, compareWith) {
  27. this._multiple = _multiple;
  28. this._emitChanges = _emitChanges;
  29. this.compareWith = compareWith;
  30. if (initiallySelectedValues && initiallySelectedValues.length) {
  31. if (_multiple) {
  32. initiallySelectedValues.forEach(value => this._markSelected(value));
  33. }
  34. else {
  35. this._markSelected(initiallySelectedValues[0]);
  36. }
  37. // Clear the array in order to avoid firing the change event for preselected values.
  38. this._selectedToEmit.length = 0;
  39. }
  40. }
  41. /**
  42. * Selects a value or an array of values.
  43. * @param values The values to select
  44. * @return Whether the selection changed as a result of this call
  45. * @breaking-change 16.0.0 make return type boolean
  46. */
  47. select(...values) {
  48. this._verifyValueAssignment(values);
  49. values.forEach(value => this._markSelected(value));
  50. const changed = this._hasQueuedChanges();
  51. this._emitChangeEvent();
  52. return changed;
  53. }
  54. /**
  55. * Deselects a value or an array of values.
  56. * @param values The values to deselect
  57. * @return Whether the selection changed as a result of this call
  58. * @breaking-change 16.0.0 make return type boolean
  59. */
  60. deselect(...values) {
  61. this._verifyValueAssignment(values);
  62. values.forEach(value => this._unmarkSelected(value));
  63. const changed = this._hasQueuedChanges();
  64. this._emitChangeEvent();
  65. return changed;
  66. }
  67. /**
  68. * Sets the selected values
  69. * @param values The new selected values
  70. * @return Whether the selection changed as a result of this call
  71. * @breaking-change 16.0.0 make return type boolean
  72. */
  73. setSelection(...values) {
  74. this._verifyValueAssignment(values);
  75. const oldValues = this.selected;
  76. const newSelectedSet = new Set(values.map(value => this._getConcreteValue(value)));
  77. values.forEach(value => this._markSelected(value));
  78. oldValues
  79. .filter(value => !newSelectedSet.has(this._getConcreteValue(value, newSelectedSet)))
  80. .forEach(value => this._unmarkSelected(value));
  81. const changed = this._hasQueuedChanges();
  82. this._emitChangeEvent();
  83. return changed;
  84. }
  85. /**
  86. * Toggles a value between selected and deselected.
  87. * @param value The value to toggle
  88. * @return Whether the selection changed as a result of this call
  89. * @breaking-change 16.0.0 make return type boolean
  90. */
  91. toggle(value) {
  92. return this.isSelected(value) ? this.deselect(value) : this.select(value);
  93. }
  94. /**
  95. * Clears all of the selected values.
  96. * @param flushEvent Whether to flush the changes in an event.
  97. * If false, the changes to the selection will be flushed along with the next event.
  98. * @return Whether the selection changed as a result of this call
  99. * @breaking-change 16.0.0 make return type boolean
  100. */
  101. clear(flushEvent = true) {
  102. this._unmarkAll();
  103. const changed = this._hasQueuedChanges();
  104. if (flushEvent) {
  105. this._emitChangeEvent();
  106. }
  107. return changed;
  108. }
  109. /**
  110. * Determines whether a value is selected.
  111. */
  112. isSelected(value) {
  113. return this._selection.has(this._getConcreteValue(value));
  114. }
  115. /**
  116. * Determines whether the model does not have a value.
  117. */
  118. isEmpty() {
  119. return this._selection.size === 0;
  120. }
  121. /**
  122. * Determines whether the model has a value.
  123. */
  124. hasValue() {
  125. return !this.isEmpty();
  126. }
  127. /**
  128. * Sorts the selected values based on a predicate function.
  129. */
  130. sort(predicate) {
  131. if (this._multiple && this.selected) {
  132. this._selected.sort(predicate);
  133. }
  134. }
  135. /**
  136. * Gets whether multiple values can be selected.
  137. */
  138. isMultipleSelection() {
  139. return this._multiple;
  140. }
  141. /** Emits a change event and clears the records of selected and deselected values. */
  142. _emitChangeEvent() {
  143. // Clear the selected values so they can be re-cached.
  144. this._selected = null;
  145. if (this._selectedToEmit.length || this._deselectedToEmit.length) {
  146. this.changed.next({
  147. source: this,
  148. added: this._selectedToEmit,
  149. removed: this._deselectedToEmit,
  150. });
  151. this._deselectedToEmit = [];
  152. this._selectedToEmit = [];
  153. }
  154. }
  155. /** Selects a value. */
  156. _markSelected(value) {
  157. value = this._getConcreteValue(value);
  158. if (!this.isSelected(value)) {
  159. if (!this._multiple) {
  160. this._unmarkAll();
  161. }
  162. if (!this.isSelected(value)) {
  163. this._selection.add(value);
  164. }
  165. if (this._emitChanges) {
  166. this._selectedToEmit.push(value);
  167. }
  168. }
  169. }
  170. /** Deselects a value. */
  171. _unmarkSelected(value) {
  172. value = this._getConcreteValue(value);
  173. if (this.isSelected(value)) {
  174. this._selection.delete(value);
  175. if (this._emitChanges) {
  176. this._deselectedToEmit.push(value);
  177. }
  178. }
  179. }
  180. /** Clears out the selected values. */
  181. _unmarkAll() {
  182. if (!this.isEmpty()) {
  183. this._selection.forEach(value => this._unmarkSelected(value));
  184. }
  185. }
  186. /**
  187. * Verifies the value assignment and throws an error if the specified value array is
  188. * including multiple values while the selection model is not supporting multiple values.
  189. */
  190. _verifyValueAssignment(values) {
  191. if (values.length > 1 && !this._multiple && (typeof ngDevMode === 'undefined' || ngDevMode)) {
  192. throw getMultipleValuesInSingleSelectionError();
  193. }
  194. }
  195. /** Whether there are queued up change to be emitted. */
  196. _hasQueuedChanges() {
  197. return !!(this._deselectedToEmit.length || this._selectedToEmit.length);
  198. }
  199. /** Returns a value that is comparable to inputValue by applying compareWith function, returns the same inputValue otherwise. */
  200. _getConcreteValue(inputValue, selection) {
  201. if (!this.compareWith) {
  202. return inputValue;
  203. }
  204. else {
  205. selection = selection ?? this._selection;
  206. for (let selectedValue of selection) {
  207. if (this.compareWith(inputValue, selectedValue)) {
  208. return selectedValue;
  209. }
  210. }
  211. return inputValue;
  212. }
  213. }
  214. }
  215. /**
  216. * Returns an error that reports that multiple values are passed into a selection model
  217. * with a single value.
  218. * @docs-private
  219. */
  220. function getMultipleValuesInSingleSelectionError() {
  221. return Error('Cannot pass multiple values into SelectionModel with single-value mode.');
  222. }
  223. export { SelectionModel as S, getMultipleValuesInSingleSelectionError as g };
  224. //# sourceMappingURL=selection-model-CeeHVIcP.mjs.map