collections.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /**
  2. * @license
  3. * Copyright Google LLC All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. import { Observable, of, Subject } from 'rxjs';
  9. import { Injectable, ɵɵdefineInjectable } from '@angular/core';
  10. /**
  11. * @fileoverview added by tsickle
  12. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  13. */
  14. /**
  15. * @abstract
  16. * @template T
  17. */
  18. class DataSource {
  19. }
  20. /**
  21. * Checks whether an object is a data source.
  22. * @param {?} value
  23. * @return {?}
  24. */
  25. function isDataSource(value) {
  26. // Check if the value is a DataSource by observing if it has a connect function. Cannot
  27. // be checked as an `instanceof DataSource` since people could create their own sources
  28. // that match the interface, but don't extend DataSource.
  29. return value && typeof value.connect === 'function';
  30. }
  31. /**
  32. * @fileoverview added by tsickle
  33. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  34. */
  35. /**
  36. * DataSource wrapper for a native array.
  37. * @template T
  38. */
  39. class ArrayDataSource extends DataSource {
  40. /**
  41. * @param {?} _data
  42. */
  43. constructor(_data) {
  44. super();
  45. this._data = _data;
  46. }
  47. /**
  48. * @return {?}
  49. */
  50. connect() {
  51. return this._data instanceof Observable ? this._data : of(this._data);
  52. }
  53. /**
  54. * @return {?}
  55. */
  56. disconnect() { }
  57. }
  58. /**
  59. * @fileoverview added by tsickle
  60. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  61. */
  62. /**
  63. * @fileoverview added by tsickle
  64. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  65. */
  66. /**
  67. * Class to be used to power selecting one or more options from a list.
  68. * @template T
  69. */
  70. class SelectionModel {
  71. /**
  72. * @param {?=} _multiple
  73. * @param {?=} initiallySelectedValues
  74. * @param {?=} _emitChanges
  75. */
  76. constructor(_multiple = false, initiallySelectedValues, _emitChanges = true) {
  77. this._multiple = _multiple;
  78. this._emitChanges = _emitChanges;
  79. /**
  80. * Currently-selected values.
  81. */
  82. this._selection = new Set();
  83. /**
  84. * Keeps track of the deselected options that haven't been emitted by the change event.
  85. */
  86. this._deselectedToEmit = [];
  87. /**
  88. * Keeps track of the selected options that haven't been emitted by the change event.
  89. */
  90. this._selectedToEmit = [];
  91. /**
  92. * Event emitted when the value has changed.
  93. */
  94. this.changed = new Subject();
  95. /**
  96. * Event emitted when the value has changed.
  97. * @deprecated Use `changed` instead.
  98. * \@breaking-change 8.0.0 To be changed to `changed`
  99. */
  100. this.onChange = this.changed;
  101. if (initiallySelectedValues && initiallySelectedValues.length) {
  102. if (_multiple) {
  103. initiallySelectedValues.forEach((/**
  104. * @param {?} value
  105. * @return {?}
  106. */
  107. value => this._markSelected(value)));
  108. }
  109. else {
  110. this._markSelected(initiallySelectedValues[0]);
  111. }
  112. // Clear the array in order to avoid firing the change event for preselected values.
  113. this._selectedToEmit.length = 0;
  114. }
  115. }
  116. /**
  117. * Selected values.
  118. * @return {?}
  119. */
  120. get selected() {
  121. if (!this._selected) {
  122. this._selected = Array.from(this._selection.values());
  123. }
  124. return this._selected;
  125. }
  126. /**
  127. * Selects a value or an array of values.
  128. * @param {...?} values
  129. * @return {?}
  130. */
  131. select(...values) {
  132. this._verifyValueAssignment(values);
  133. values.forEach((/**
  134. * @param {?} value
  135. * @return {?}
  136. */
  137. value => this._markSelected(value)));
  138. this._emitChangeEvent();
  139. }
  140. /**
  141. * Deselects a value or an array of values.
  142. * @param {...?} values
  143. * @return {?}
  144. */
  145. deselect(...values) {
  146. this._verifyValueAssignment(values);
  147. values.forEach((/**
  148. * @param {?} value
  149. * @return {?}
  150. */
  151. value => this._unmarkSelected(value)));
  152. this._emitChangeEvent();
  153. }
  154. /**
  155. * Toggles a value between selected and deselected.
  156. * @param {?} value
  157. * @return {?}
  158. */
  159. toggle(value) {
  160. this.isSelected(value) ? this.deselect(value) : this.select(value);
  161. }
  162. /**
  163. * Clears all of the selected values.
  164. * @return {?}
  165. */
  166. clear() {
  167. this._unmarkAll();
  168. this._emitChangeEvent();
  169. }
  170. /**
  171. * Determines whether a value is selected.
  172. * @param {?} value
  173. * @return {?}
  174. */
  175. isSelected(value) {
  176. return this._selection.has(value);
  177. }
  178. /**
  179. * Determines whether the model does not have a value.
  180. * @return {?}
  181. */
  182. isEmpty() {
  183. return this._selection.size === 0;
  184. }
  185. /**
  186. * Determines whether the model has a value.
  187. * @return {?}
  188. */
  189. hasValue() {
  190. return !this.isEmpty();
  191. }
  192. /**
  193. * Sorts the selected values based on a predicate function.
  194. * @param {?=} predicate
  195. * @return {?}
  196. */
  197. sort(predicate) {
  198. if (this._multiple && this.selected) {
  199. (/** @type {?} */ (this._selected)).sort(predicate);
  200. }
  201. }
  202. /**
  203. * Gets whether multiple values can be selected.
  204. * @return {?}
  205. */
  206. isMultipleSelection() {
  207. return this._multiple;
  208. }
  209. /**
  210. * Emits a change event and clears the records of selected and deselected values.
  211. * @private
  212. * @return {?}
  213. */
  214. _emitChangeEvent() {
  215. // Clear the selected values so they can be re-cached.
  216. this._selected = null;
  217. if (this._selectedToEmit.length || this._deselectedToEmit.length) {
  218. this.changed.next({
  219. source: this,
  220. added: this._selectedToEmit,
  221. removed: this._deselectedToEmit
  222. });
  223. this._deselectedToEmit = [];
  224. this._selectedToEmit = [];
  225. }
  226. }
  227. /**
  228. * Selects a value.
  229. * @private
  230. * @param {?} value
  231. * @return {?}
  232. */
  233. _markSelected(value) {
  234. if (!this.isSelected(value)) {
  235. if (!this._multiple) {
  236. this._unmarkAll();
  237. }
  238. this._selection.add(value);
  239. if (this._emitChanges) {
  240. this._selectedToEmit.push(value);
  241. }
  242. }
  243. }
  244. /**
  245. * Deselects a value.
  246. * @private
  247. * @param {?} value
  248. * @return {?}
  249. */
  250. _unmarkSelected(value) {
  251. if (this.isSelected(value)) {
  252. this._selection.delete(value);
  253. if (this._emitChanges) {
  254. this._deselectedToEmit.push(value);
  255. }
  256. }
  257. }
  258. /**
  259. * Clears out the selected values.
  260. * @private
  261. * @return {?}
  262. */
  263. _unmarkAll() {
  264. if (!this.isEmpty()) {
  265. this._selection.forEach((/**
  266. * @param {?} value
  267. * @return {?}
  268. */
  269. value => this._unmarkSelected(value)));
  270. }
  271. }
  272. /**
  273. * Verifies the value assignment and throws an error if the specified value array is
  274. * including multiple values while the selection model is not supporting multiple values.
  275. * @private
  276. * @param {?} values
  277. * @return {?}
  278. */
  279. _verifyValueAssignment(values) {
  280. if (values.length > 1 && !this._multiple) {
  281. throw getMultipleValuesInSingleSelectionError();
  282. }
  283. }
  284. }
  285. /**
  286. * Returns an error that reports that multiple values are passed into a selection model
  287. * with a single value.
  288. * \@docs-private
  289. * @return {?}
  290. */
  291. function getMultipleValuesInSingleSelectionError() {
  292. return Error('Cannot pass multiple values into SelectionModel with single-value mode.');
  293. }
  294. /**
  295. * @fileoverview added by tsickle
  296. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  297. */
  298. /**
  299. * Class to coordinate unique selection based on name.
  300. * Intended to be consumed as an Angular service.
  301. * This service is needed because native radio change events are only fired on the item currently
  302. * being selected, and we still need to uncheck the previous selection.
  303. *
  304. * This service does not *store* any IDs and names because they may change at any time, so it is
  305. * less error-prone if they are simply passed through when the events occur.
  306. */
  307. class UniqueSelectionDispatcher {
  308. constructor() {
  309. this._listeners = [];
  310. }
  311. /**
  312. * Notify other items that selection for the given name has been set.
  313. * @param {?} id ID of the item.
  314. * @param {?} name Name of the item.
  315. * @return {?}
  316. */
  317. notify(id, name) {
  318. for (let listener of this._listeners) {
  319. listener(id, name);
  320. }
  321. }
  322. /**
  323. * Listen for future changes to item selection.
  324. * @param {?} listener
  325. * @return {?} Function used to deregister listener
  326. */
  327. listen(listener) {
  328. this._listeners.push(listener);
  329. return (/**
  330. * @return {?}
  331. */
  332. () => {
  333. this._listeners = this._listeners.filter((/**
  334. * @param {?} registered
  335. * @return {?}
  336. */
  337. (registered) => {
  338. return listener !== registered;
  339. }));
  340. });
  341. }
  342. /**
  343. * @return {?}
  344. */
  345. ngOnDestroy() {
  346. this._listeners = [];
  347. }
  348. }
  349. UniqueSelectionDispatcher.decorators = [
  350. { type: Injectable, args: [{ providedIn: 'root' },] },
  351. ];
  352. /** @nocollapse */ UniqueSelectionDispatcher.ngInjectableDef = ɵɵdefineInjectable({ factory: function UniqueSelectionDispatcher_Factory() { return new UniqueSelectionDispatcher(); }, token: UniqueSelectionDispatcher, providedIn: "root" });
  353. /**
  354. * @fileoverview added by tsickle
  355. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  356. */
  357. /**
  358. * @fileoverview added by tsickle
  359. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  360. */
  361. /**
  362. * @fileoverview added by tsickle
  363. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  364. */
  365. export { UniqueSelectionDispatcher, ArrayDataSource, isDataSource, DataSource, getMultipleValuesInSingleSelectionError, SelectionModel };
  366. //# sourceMappingURL=collections.js.map