@lenne.tech/ng-base 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,7 @@ import * as i1$1 from '@angular/router';
14
14
  import { Router } from '@angular/router';
15
15
  import { buildClientSchema, getIntrospectionQuery } from 'graphql';
16
16
  import { map } from 'rxjs/operators';
17
+ import { __awaiter } from 'tslib';
17
18
 
18
19
  const BASE_MODULE_CONFIG = new InjectionToken('BASE_MODULE_CONFIG');
19
20
 
@@ -1629,14 +1630,23 @@ class GraphQLService {
1629
1630
  /**
1630
1631
  * Get fields / args from IGraphQLTypeCollection
1631
1632
  */
1632
- graphQLTypeCollectionToStringArray(collection, current = '', result = []) {
1633
+ graphQLTypeCollectionToStringArray(collection, current = '', result = [], cacheNode = []) {
1633
1634
  if (collection) {
1634
1635
  if (collection instanceof GraphQLType) {
1635
1636
  result.push(current);
1636
1637
  }
1637
1638
  else {
1639
+ if (current === null || current === void 0 ? void 0 : current.includes('.')) {
1640
+ cacheNode.push(current.split('.')[1]);
1641
+ }
1642
+ else if (current) {
1643
+ cacheNode.push(current);
1644
+ }
1638
1645
  for (const key of Object.keys(collection)) {
1639
- this.graphQLTypeCollectionToStringArray(collection[key], current ? current + '.' + key : key, result);
1646
+ if (current === key || cacheNode.includes(key)) {
1647
+ continue;
1648
+ }
1649
+ this.graphQLTypeCollectionToStringArray(collection[key], current ? current + '.' + key : key, result, cacheNode);
1640
1650
  }
1641
1651
  }
1642
1652
  }
@@ -1688,6 +1698,10 @@ class GraphQLService {
1688
1698
  }
1689
1699
  // Process object
1690
1700
  else if (typeof args === 'object') {
1701
+ // Check object is empty
1702
+ if (args && Object.keys(args).length === 0 && Object.getPrototypeOf(args) === Object.prototype) {
1703
+ return { argsString: '{}', schemaArgs, usedArgs };
1704
+ }
1691
1705
  // Process all object entries
1692
1706
  for (const [key, value] of Object.entries(args)) {
1693
1707
  // Init data for current entry
@@ -1991,31 +2005,73 @@ class ScrollService {
1991
2005
  this.router = router;
1992
2006
  this.moduleConfig = moduleConfig;
1993
2007
  /**
1994
- * Scroll offset
2008
+ * Is a scroll listener set
2009
+ *
2010
+ * @protected
1995
2011
  */
1996
- this.scrollOffset = 100;
2012
+ this.scrollListener = false;
1997
2013
  /**
1998
- * Detection offset
1999
- * Should be >= scrollOffset
2014
+ * Current scroll timer;
2015
+ *
2016
+ * @protected
2000
2017
  */
2001
- this.detectionOffset = 200;
2002
- const config = Object.assign({ scrollDetectionOffset: 200, scrollOffset: 100 }, moduleConfig);
2018
+ this.scrollTimer = -1;
2019
+ const config = Object.assign({ scrollDetectionOffset: 200, scrollOffset: 100, scrollSamplingRate: 100 }, moduleConfig);
2003
2020
  this.detectionOffset = config.scrollDetectionOffset;
2004
2021
  this.scrollOffset = config.scrollOffset;
2022
+ this.scrollSamplingRate = config.scrollSamplingRate;
2005
2023
  }
2006
2024
  /**
2007
2025
  * Scroll to structureElement with specific ID
2008
2026
  */
2009
2027
  scrollTo(id, options = {}) {
2010
- options = Object.assign({ stopPropagation: true }, options);
2011
- if (this.router.url !== '/' && !this.router.url.startsWith('/#')) {
2012
- this.router.navigate(['/'], { fragment: id });
2013
- return options.stopPropagation ? false : true;
2014
- }
2015
- const el = document.getElementById(id);
2016
- const cords = el.getBoundingClientRect();
2017
- window.scrollBy({ top: cords.top - this.scrollOffset, behavior: 'smooth' });
2018
- return options.stopPropagation ? false : true;
2028
+ return __awaiter(this, void 0, void 0, function* () {
2029
+ // Prepare config
2030
+ const config = Object.assign({ checkUrl: true, loadImagesBefore: true, resetStyles: false, setOverflow: true, stopPropagation: true }, options);
2031
+ // Check current URL
2032
+ if (config.checkUrl && this.router.url !== '/' && !this.router.url.startsWith('/#')) {
2033
+ yield this.router.navigate(['/'], { fragment: id });
2034
+ return !config.stopPropagation;
2035
+ }
2036
+ // Check if target element exits
2037
+ const el = document.getElementById(id);
2038
+ if (!el) {
2039
+ console.error('Missing scroll element with ID:', id);
2040
+ return !config.stopPropagation;
2041
+ }
2042
+ // Check if body overflow is visible
2043
+ // This is necessary for scroll handling
2044
+ const body = document.querySelector('body');
2045
+ let undoOverflowChanges = false;
2046
+ const scrollBodyStyles = JSON.parse(JSON.stringify(window.getComputedStyle(body)));
2047
+ if (config.setOverflow && (scrollBodyStyles.overflowX !== 'visible' || scrollBodyStyles.overflowY !== 'visible')) {
2048
+ // Set body overflow to visible
2049
+ console.warn('Style of body element for overflowX and overflowY will be set to "visible" for scrolling!');
2050
+ body.style.overflowX = 'visible';
2051
+ body.style.overflowY = 'visible';
2052
+ undoOverflowChanges = true;
2053
+ }
2054
+ // Load images before scrolling (once)
2055
+ if (config.loadImagesBefore) {
2056
+ yield this.preLoadImages();
2057
+ }
2058
+ // Get get bounding rect of target element
2059
+ const cords = el.getBoundingClientRect();
2060
+ // Scroll to element
2061
+ window.scrollBy({ top: cords.top - this.scrollOffset, behavior: 'smooth' });
2062
+ yield this.scrollDone({
2063
+ doneFunction: () => {
2064
+ // Undo changes
2065
+ if (undoOverflowChanges && config.resetStyles) {
2066
+ body.style.overflowX = scrollBodyStyles.overflowX;
2067
+ body.style.overflowY = scrollBodyStyles.overflowY;
2068
+ console.warn('Original style of body element for overflowX and overflowY is set.');
2069
+ }
2070
+ },
2071
+ });
2072
+ // Return stop propagation configuration
2073
+ return !config.stopPropagation;
2074
+ });
2019
2075
  }
2020
2076
  /**
2021
2077
  * Get last active structureElement
@@ -2045,6 +2101,148 @@ class ScrollService {
2045
2101
  const lastActiveElement = this.getLastActiveElement(list);
2046
2102
  return id === lastActiveElement || (lastActiveElement === null && isFirst);
2047
2103
  }
2104
+ /**
2105
+ * Scroll done promise
2106
+ */
2107
+ scrollDone(options = {}) {
2108
+ // Init configuration
2109
+ const config = Object.assign({ doneFunction: undefined, timeout: undefined }, options);
2110
+ // Return promise for scroll done
2111
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
2112
+ // Init variables
2113
+ let rejected = false;
2114
+ let timeoutId;
2115
+ // Set timeout handling if necessary
2116
+ if (config.timeout) {
2117
+ timeoutId = window.setTimeout(() => {
2118
+ rejected = true;
2119
+ reject(new Error('Timeout for scrollDone after:' + config.timeout));
2120
+ }, config.timeout);
2121
+ }
2122
+ // Wait for scroll done
2123
+ yield this.scrollDoneDetection();
2124
+ // Resolve promise if not already rejected
2125
+ if (!rejected) {
2126
+ // Stop timeout if set
2127
+ if (timeoutId) {
2128
+ window.clearTimeout(timeoutId);
2129
+ }
2130
+ // Get result (if doneFunction is set)
2131
+ let result;
2132
+ if (config.doneFunction) {
2133
+ result = yield config.doneFunction();
2134
+ }
2135
+ // Scroll done
2136
+ resolve(result);
2137
+ }
2138
+ }));
2139
+ }
2140
+ /**
2141
+ * Scroll done detecton
2142
+ *
2143
+ * @protected
2144
+ */
2145
+ scrollDoneDetection() {
2146
+ // Return current scroll promise (Singelton)
2147
+ if (this.scrollPromise) {
2148
+ return this.scrollPromise;
2149
+ }
2150
+ // Create current scroll promise
2151
+ this.scrollPromise = new Promise((resolve) => {
2152
+ this.scrollPromiseResolve = resolve;
2153
+ if (this.scrollTimer !== -1) {
2154
+ window.clearTimeout(this.scrollTimer);
2155
+ }
2156
+ this.scrollTimer = window.setTimeout(() => {
2157
+ window.removeEventListener('scroll', this.scrollDoneDetection.bind(this));
2158
+ this.scrollPromise = undefined;
2159
+ resolve();
2160
+ }, this.scrollSamplingRate);
2161
+ });
2162
+ // Lisitening to scroll event;
2163
+ window.addEventListener('scroll', this.scrollDoneDetection.bind(this));
2164
+ // Return promise
2165
+ return this.scrollPromise;
2166
+ }
2167
+ /**
2168
+ * Preaload images (for lazy handling)
2169
+ *
2170
+ * @param options
2171
+ * @protected
2172
+ */
2173
+ preLoadImages(options) {
2174
+ // Init configuration
2175
+ const config = Object.assign({ doneFunnction: undefined, timeout: undefined }, options);
2176
+ return new Promise((resolve) => {
2177
+ // Init variables
2178
+ let resolved = false;
2179
+ let timeoutId;
2180
+ // Init loading
2181
+ const images = Array.from(document.getElementsByTagName('img'));
2182
+ const promises = [];
2183
+ // Prepare images
2184
+ for (const image of images) {
2185
+ const src = image.getAttribute('src');
2186
+ // Check if changes are necessary and possible
2187
+ if ((image.complete && image.naturalHeight !== 0) || image.getAttribute('loading') !== 'lazy' || !src) {
2188
+ continue;
2189
+ }
2190
+ const newImage = document.createElement('img');
2191
+ Array.from(image.attributes).forEach((attr) => {
2192
+ if (attr.name !== 'loading' && attr.name !== 'src') {
2193
+ newImage.setAttribute(attr.name, attr.value);
2194
+ }
2195
+ });
2196
+ image.replaceWith(newImage);
2197
+ promises.push(this.loadImage(newImage, src));
2198
+ }
2199
+ if (config.timeout) {
2200
+ timeoutId = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
2201
+ resolved = true;
2202
+ if (config.doneFunnction) {
2203
+ yield config.doneFunction();
2204
+ }
2205
+ resolve(undefined);
2206
+ }));
2207
+ }
2208
+ // Wait until all images loaded
2209
+ Promise.all(promises)
2210
+ .catch((e) => console.error('Error during image loading', e))
2211
+ .finally(() => __awaiter(this, void 0, void 0, function* () {
2212
+ // Check if already resolved
2213
+ if (resolved) {
2214
+ return;
2215
+ }
2216
+ // Stop timeout
2217
+ if (timeoutId) {
2218
+ clearTimeout(timeoutId);
2219
+ }
2220
+ // Return result
2221
+ let result;
2222
+ if (config.doneFunnction) {
2223
+ result = yield config.doneFunction();
2224
+ }
2225
+ resolve(result);
2226
+ }));
2227
+ });
2228
+ }
2229
+ /**
2230
+ * Load image for image element
2231
+ *
2232
+ * @param imageElement
2233
+ * @param src
2234
+ * @protected
2235
+ */
2236
+ loadImage(imageElement, src) {
2237
+ return __awaiter(this, void 0, void 0, function* () {
2238
+ src = src || imageElement.src;
2239
+ return new Promise((resolve, reject) => {
2240
+ imageElement.onload = () => resolve(imageElement);
2241
+ imageElement.onerror = reject;
2242
+ imageElement.src = src;
2243
+ });
2244
+ });
2245
+ }
2048
2246
  }
2049
2247
  ScrollService.ɵprov = i0.ɵɵdefineInjectable({ factory: function ScrollService_Factory() { return new ScrollService(i0.ɵɵinject(i1$1.Router), i0.ɵɵinject(BASE_MODULE_CONFIG)); }, token: ScrollService, providedIn: "root" });
2050
2248
  ScrollService.decorators = [