@lenne.tech/ng-base 0.0.11 → 0.2.2

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.
Files changed (26) hide show
  1. package/bundles/lenne.tech-ng-base-src-lib-base.umd.js +338 -32
  2. package/bundles/lenne.tech-ng-base-src-lib-base.umd.js.map +1 -1
  3. package/bundles/lenne.tech-ng-base.umd.js +338 -32
  4. package/bundles/lenne.tech-ng-base.umd.js.map +1 -1
  5. package/esm2015/lib/base/factories/apollo-options.factory.js +43 -4
  6. package/esm2015/lib/base/interfaces/base-module-config.interface.js +1 -1
  7. package/esm2015/lib/base/services/graphql-plus.service.js +5 -2
  8. package/esm2015/lib/base/services/graphql.service.js +37 -6
  9. package/esm2015/lib/base/services/scroll.service.js +201 -16
  10. package/esm2015/src/lib/base/factories/apollo-options.factory.js +43 -4
  11. package/esm2015/src/lib/base/interfaces/base-module-config.interface.js +1 -1
  12. package/esm2015/src/lib/base/services/graphql-plus.service.js +5 -2
  13. package/esm2015/src/lib/base/services/graphql.service.js +37 -6
  14. package/esm2015/src/lib/base/services/scroll.service.js +201 -16
  15. package/fesm2015/lenne.tech-ng-base-src-lib-base.js +281 -24
  16. package/fesm2015/lenne.tech-ng-base-src-lib-base.js.map +1 -1
  17. package/fesm2015/lenne.tech-ng-base.js +281 -24
  18. package/fesm2015/lenne.tech-ng-base.js.map +1 -1
  19. package/lib/base/interfaces/base-module-config.interface.d.ts +9 -0
  20. package/lib/base/services/graphql.service.d.ts +1 -1
  21. package/lib/base/services/scroll.service.d.ts +66 -3
  22. package/package.json +1 -1
  23. package/src/lib/base/interfaces/base-module-config.interface.d.ts +9 -0
  24. package/src/lib/base/lenne.tech-ng-base-src-lib-base.metadata.json +1 -1
  25. package/src/lib/base/services/graphql.service.d.ts +1 -1
  26. package/src/lib/base/services/scroll.service.d.ts +66 -3
@@ -4,14 +4,17 @@ import { HttpLink } from 'apollo-angular/http';
4
4
  import * as i0 from '@angular/core';
5
5
  import { InjectionToken, Injectable, Inject, Directive, ElementRef, Input, Output, HostBinding, HostListener, Pipe, NgModule } from '@angular/core';
6
6
  import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
7
- import { ApolloLink } from '@apollo/client/core';
7
+ import { ApolloLink, split, concat } from '@apollo/client/core';
8
8
  import { InMemoryCache } from '@apollo/client/cache';
9
+ import { WebSocketLink } from '@apollo/client/link/ws';
10
+ import { getMainDefinition } from '@apollo/client/utilities';
9
11
  import * as i1$2 from '@angular/platform-browser';
10
12
  import { DomSanitizer, Title, Meta } from '@angular/platform-browser';
11
13
  import * as i1$1 from '@angular/router';
12
14
  import { Router } from '@angular/router';
13
15
  import { buildClientSchema, getIntrospectionQuery } from 'graphql';
14
16
  import { map } from 'rxjs/operators';
17
+ import { __awaiter } from 'tslib';
15
18
 
16
19
  const BASE_MODULE_CONFIG = new InjectionToken('BASE_MODULE_CONFIG');
17
20
 
@@ -208,7 +211,8 @@ AuthService.ctorParameters = () => [
208
211
  * Factory for apollo-angular options
209
212
  */
210
213
  function apolloOptionsFactory(baseModuleConfig, httpLink, authService) {
211
- const link = httpLink.create({
214
+ const links = [];
215
+ const http = httpLink.create({
212
216
  uri: baseModuleConfig.apiUrl,
213
217
  });
214
218
  const authMiddleware = new ApolloLink((operation, forward) => {
@@ -222,8 +226,44 @@ function apolloOptionsFactory(baseModuleConfig, httpLink, authService) {
222
226
  }
223
227
  return forward(operation);
224
228
  });
229
+ if (baseModuleConfig.wsUrl) {
230
+ let wsClient;
231
+ if (localStorage) {
232
+ const token = authService.token || null;
233
+ wsClient = new WebSocketLink({
234
+ uri: baseModuleConfig.wsUrl,
235
+ options: {
236
+ reconnect: true,
237
+ connectionParams: {
238
+ authToken: token,
239
+ },
240
+ },
241
+ });
242
+ }
243
+ else {
244
+ wsClient = new WebSocketLink({
245
+ uri: baseModuleConfig.wsUrl,
246
+ options: {
247
+ reconnect: true,
248
+ },
249
+ });
250
+ }
251
+ // using the ability to split links, you can send data to each link
252
+ // depending on what kind of operation is being sent
253
+ links.push(split(
254
+ // split based on operation type
255
+ ({ query }) => {
256
+ // @ts-ignore
257
+ const { kind, operation } = getMainDefinition(query);
258
+ return kind === 'OperationDefinition' && operation === 'subscription';
259
+ }, wsClient, concat(authMiddleware, http)));
260
+ }
261
+ else {
262
+ links.push(http);
263
+ links.push(authMiddleware);
264
+ }
225
265
  return {
226
- link: ApolloLink.from([authMiddleware, link]),
266
+ link: ApolloLink.from(links),
227
267
  cache: new InMemoryCache(),
228
268
  defaultOptions: {
229
269
  query: {
@@ -1536,13 +1576,29 @@ class GraphQLService {
1536
1576
  const documentNode = fields
1537
1577
  ? gql(config.type + '{\n' + graphql + args + fields + '\n}')
1538
1578
  : gql(config.type + '{\n' + graphql + args + '\n}');
1579
+ // Log
1580
+ if (config.log) {
1581
+ console.log({ documentNode });
1582
+ }
1539
1583
  const request = {};
1540
1584
  request[config.type] = documentNode;
1541
1585
  // Log
1542
1586
  if (config.log) {
1543
1587
  console.log({ request });
1544
1588
  }
1545
- const func = config.type === GraphQLRequestType.MUTATION ? 'mutate' : config.type;
1589
+ let func;
1590
+ if (config.type === GraphQLRequestType.MUTATION) {
1591
+ func = 'mutate';
1592
+ request[config.type] = documentNode;
1593
+ }
1594
+ else if (config.type === GraphQLRequestType.SUBSCRIPTION) {
1595
+ func = 'subscribe';
1596
+ request.query = documentNode;
1597
+ }
1598
+ else {
1599
+ func = config.type;
1600
+ request[config.type] = documentNode;
1601
+ }
1546
1602
  this.apollo[func](request).subscribe((result) => {
1547
1603
  var _a;
1548
1604
  const data = ((_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a[graphql]) !== undefined ? result.data[graphql] : result;
@@ -1560,8 +1616,10 @@ class GraphQLService {
1560
1616
  else {
1561
1617
  subscriber.next(config.model.map(data));
1562
1618
  }
1563
- // Done
1564
- subscriber.complete();
1619
+ if (config.type !== GraphQLRequestType.SUBSCRIPTION) {
1620
+ // Done
1621
+ subscriber.complete();
1622
+ }
1565
1623
  }, (error) => {
1566
1624
  subscriber.error(error);
1567
1625
  subscriber.complete();
@@ -1572,14 +1630,23 @@ class GraphQLService {
1572
1630
  /**
1573
1631
  * Get fields / args from IGraphQLTypeCollection
1574
1632
  */
1575
- graphQLTypeCollectionToStringArray(collection, current = '', result = []) {
1633
+ graphQLTypeCollectionToStringArray(collection, current = '', result = [], cacheNode = []) {
1576
1634
  if (collection) {
1577
1635
  if (collection instanceof GraphQLType) {
1578
1636
  result.push(current);
1579
1637
  }
1580
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
+ }
1581
1645
  for (const key of Object.keys(collection)) {
1582
- 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);
1583
1650
  }
1584
1651
  }
1585
1652
  }
@@ -1631,6 +1698,10 @@ class GraphQLService {
1631
1698
  }
1632
1699
  // Process object
1633
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
+ }
1634
1705
  // Process all object entries
1635
1706
  for (const [key, value] of Object.entries(args)) {
1636
1707
  // Init data for current entry
@@ -1869,7 +1940,9 @@ class GraphQLPlusService extends GraphQLService {
1869
1940
  }
1870
1941
  subscriber.next(response);
1871
1942
  this.handleLoader('stop', options);
1872
- subscriber.complete();
1943
+ if (options.type !== GraphQLRequestType.SUBSCRIPTION) {
1944
+ subscriber.complete();
1945
+ }
1873
1946
  }, (error) => {
1874
1947
  if (!options.ignoreErrors) {
1875
1948
  this.handleError(error, options.excludedErrors);
@@ -1932,31 +2005,73 @@ class ScrollService {
1932
2005
  this.router = router;
1933
2006
  this.moduleConfig = moduleConfig;
1934
2007
  /**
1935
- * Scroll offset
2008
+ * Is a scroll listener set
2009
+ *
2010
+ * @protected
1936
2011
  */
1937
- this.scrollOffset = 100;
2012
+ this.scrollListener = false;
1938
2013
  /**
1939
- * Detection offset
1940
- * Should be >= scrollOffset
2014
+ * Current scroll timer;
2015
+ *
2016
+ * @protected
1941
2017
  */
1942
- this.detectionOffset = 200;
1943
- 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);
1944
2020
  this.detectionOffset = config.scrollDetectionOffset;
1945
2021
  this.scrollOffset = config.scrollOffset;
2022
+ this.scrollSamplingRate = config.scrollSamplingRate;
1946
2023
  }
1947
2024
  /**
1948
2025
  * Scroll to structureElement with specific ID
1949
2026
  */
1950
2027
  scrollTo(id, options = {}) {
1951
- options = Object.assign({ stopPropagation: true }, options);
1952
- if (this.router.url !== '/' && !this.router.url.startsWith('/#')) {
1953
- this.router.navigate(['/'], { fragment: id });
1954
- return options.stopPropagation ? false : true;
1955
- }
1956
- const el = document.getElementById(id);
1957
- const cords = el.getBoundingClientRect();
1958
- window.scrollBy({ top: cords.top - this.scrollOffset, behavior: 'smooth' });
1959
- 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
+ });
1960
2075
  }
1961
2076
  /**
1962
2077
  * Get last active structureElement
@@ -1986,6 +2101,148 @@ class ScrollService {
1986
2101
  const lastActiveElement = this.getLastActiveElement(list);
1987
2102
  return id === lastActiveElement || (lastActiveElement === null && isFirst);
1988
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
+ }
1989
2246
  }
1990
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" });
1991
2248
  ScrollService.decorators = [