| 
									
										
										
										
											2019-08-31 20:51:14 -07:00
										 |  |  | import { get, getProperties } from '@ember/object'; | 
					
						
							|  |  |  | import { isEqual } from '@ember/utils'; | 
					
						
							| 
									
										
										
										
											2020-11-09 12:17:51 -08:00
										 |  |  | import { noop } from 'lodash-es'; | 
					
						
							| 
									
										
										
										
											2020-08-26 15:44:50 -07:00
										 |  |  | import Component from '@ember/component'; | 
					
						
							| 
									
										
										
										
											2019-08-31 20:51:14 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Will compare 1 level down properties (return of getProperties); | 
					
						
							|  |  |  |  * @param previousProp reference to the old property by name | 
					
						
							|  |  |  |  * @param newProp new reference to the property with same name as previousProp | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const hasEqualProperties = <T extends Record<string, unknown>>(previousProp: T, newProp: T): boolean => { | 
					
						
							|  |  |  |   if (!previousProp || !newProp) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 15:44:50 -07:00
										 |  |  |   return Object.keys(previousProp).every((key: keyof T): boolean => isEqual(previousProp[key], newProp[key])); | 
					
						
							| 
									
										
										
										
											2019-08-31 20:51:14 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Invokes the container's task to fetch data on document insertion and successive attribute updates. | 
					
						
							|  |  |  |  * You need to specify which attributes can trigger fetch data task as a 2nd param. | 
					
						
							| 
									
										
										
										
											2020-08-26 15:44:50 -07:00
										 |  |  |  * @template T extends Component the component to augment with data source behavior | 
					
						
							|  |  |  |  * @param {string} containerDataTaskName name of the attribute on the container component that | 
					
						
							|  |  |  |  * references the ember-concurrency data fetching task to be invoked for component | 
					
						
							|  |  |  |  * life-cycle hooks didInsertElement & didUpdateAttrs | 
					
						
							| 
									
										
										
										
											2019-08-31 20:51:14 -07:00
										 |  |  |  * @param {Array<keyof T>} attrs attributes to 'watch' on didUpdateAttrs to perform task or not. | 
					
						
							|  |  |  |  * @param {Array<any>} params optional list of arguments for the task generator function | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-08-26 15:44:50 -07:00
										 |  |  | export const containerDataSource = <T extends Pick<Component, 'didInsertElement' | 'didUpdateAttrs'>>( | 
					
						
							|  |  |  |   containerDataTaskName: keyof T, | 
					
						
							| 
									
										
										
										
											2019-08-31 20:51:14 -07:00
										 |  |  |   attrs: Array<keyof T>, | 
					
						
							|  |  |  |   // eslint-disable-next-line
 | 
					
						
							|  |  |  |   ...params: Array<any> | 
					
						
							| 
									
										
										
										
											2020-08-26 15:44:50 -07:00
										 |  |  | ): ClassDecorator => (klass): void => { | 
					
						
							|  |  |  |   const taskPerformer = (lifeCycleMethod: () => void): (() => void) => | 
					
						
							| 
									
										
										
										
											2019-08-31 20:51:14 -07:00
										 |  |  |     // eslint-disable-next-line
 | 
					
						
							|  |  |  |     function(this: T & { _lastContainerDataSourceAttrs?: any }, ...args: Array<any>) { | 
					
						
							|  |  |  |       const newAttrs = getProperties(this, attrs); | 
					
						
							|  |  |  |       const lastAttrs = this._lastContainerDataSourceAttrs; | 
					
						
							|  |  |  |       this._lastContainerDataSourceAttrs = newAttrs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!lastAttrs || !hasEqualProperties(lastAttrs, newAttrs)) { | 
					
						
							|  |  |  |         // e-concurrency does not currently support dot notation access for CPs, hence Ember.get
 | 
					
						
							|  |  |  |         // eslint-disable-next-line
 | 
					
						
							|  |  |  |         (<any>get(this, <Extract<keyof T, string>>containerDataTaskName)).perform(...params); | 
					
						
							|  |  |  |         lifeCycleMethod && lifeCycleMethod.apply(this, ...args); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   klass.prototype.didInsertElement = taskPerformer(klass.prototype.didInsertElement); | 
					
						
							|  |  |  |   klass.prototype.didUpdateAttrs = taskPerformer(noop); | 
					
						
							|  |  |  | }; |