@lenne.tech/ng-base 0.2.1 → 0.3.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 (30) hide show
  1. package/bundles/lenne.tech-ng-base-src-lib-base.umd.js +374 -46
  2. package/bundles/lenne.tech-ng-base-src-lib-base.umd.js.map +1 -1
  3. package/bundles/lenne.tech-ng-base.umd.js +374 -46
  4. package/bundles/lenne.tech-ng-base.umd.js.map +1 -1
  5. package/esm2015/lib/base/directives/match-height.directive.js +45 -29
  6. package/esm2015/lib/base/interfaces/base-module-config.interface.js +1 -1
  7. package/esm2015/lib/base/public-api.js +2 -1
  8. package/esm2015/lib/base/services/image.service.js +113 -0
  9. package/esm2015/lib/base/services/scroll.service.js +129 -19
  10. package/esm2015/src/lib/base/directives/match-height.directive.js +45 -29
  11. package/esm2015/src/lib/base/interfaces/base-module-config.interface.js +1 -1
  12. package/esm2015/src/lib/base/public-api.js +2 -1
  13. package/esm2015/src/lib/base/services/image.service.js +113 -0
  14. package/esm2015/src/lib/base/services/scroll.service.js +129 -19
  15. package/fesm2015/lenne.tech-ng-base-src-lib-base.js +279 -47
  16. package/fesm2015/lenne.tech-ng-base-src-lib-base.js.map +1 -1
  17. package/fesm2015/lenne.tech-ng-base.js +279 -47
  18. package/fesm2015/lenne.tech-ng-base.js.map +1 -1
  19. package/lib/base/directives/match-height.directive.d.ts +7 -3
  20. package/lib/base/interfaces/base-module-config.interface.d.ts +5 -0
  21. package/lib/base/public-api.d.ts +1 -0
  22. package/lib/base/services/image.service.d.ts +25 -0
  23. package/lib/base/services/scroll.service.d.ts +53 -6
  24. package/package.json +12 -11
  25. package/src/lib/base/directives/match-height.directive.d.ts +7 -3
  26. package/src/lib/base/interfaces/base-module-config.interface.d.ts +5 -0
  27. package/src/lib/base/lenne.tech-ng-base-src-lib-base.metadata.json +1 -1
  28. package/src/lib/base/public-api.d.ts +1 -0
  29. package/src/lib/base/services/image.service.d.ts +25 -0
  30. package/src/lib/base/services/scroll.service.d.ts +53 -6
@@ -2,12 +2,13 @@ import * as i1 from 'apollo-angular';
2
2
  import { APOLLO_OPTIONS, gql, Apollo } from 'apollo-angular';
3
3
  import { HttpLink } from 'apollo-angular/http';
4
4
  import * as i0 from '@angular/core';
5
- import { InjectionToken, Injectable, Inject, Directive, ElementRef, Input, Output, HostBinding, HostListener, Pipe, NgModule } from '@angular/core';
5
+ import { InjectionToken, Injectable, Inject, Directive, ElementRef, Input, Output, HostBinding, NgZone, HostListener, Pipe, NgModule } from '@angular/core';
6
6
  import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
7
7
  import { ApolloLink, split, concat } from '@apollo/client/core';
8
8
  import { InMemoryCache } from '@apollo/client/cache';
9
9
  import { WebSocketLink } from '@apollo/client/link/ws';
10
10
  import { getMainDefinition } from '@apollo/client/utilities';
11
+ import { __awaiter } from 'tslib';
11
12
  import * as i1$2 from '@angular/platform-browser';
12
13
  import { DomSanitizer, Title, Meta } from '@angular/platform-browser';
13
14
  import * as i1$1 from '@angular/router';
@@ -417,12 +418,125 @@ LazyLoadDirective.propDecorators = {
417
418
  src: [{ type: Input }]
418
419
  };
419
420
 
421
+ /**
422
+ * Image service
423
+ */
424
+ class ImageService {
425
+ /**
426
+ * Preaload images (for lazy handling)
427
+ *
428
+ * @param options
429
+ * @protected
430
+ */
431
+ preLoadImages(options) {
432
+ // Init configuration
433
+ const config = Object.assign({ doneFunnction: undefined, elements: document, regExp: undefined, timeout: undefined }, options);
434
+ return new Promise((resolve) => {
435
+ var _a;
436
+ // Init variables
437
+ let resolved = false;
438
+ let timeoutId;
439
+ // HTMLCollection to array
440
+ if (config.elements instanceof HTMLCollection) {
441
+ config.elements = Array.from(config.elements);
442
+ }
443
+ // Single element to array
444
+ if (!Array.isArray(config.elements)) {
445
+ config.elements = [config.elements];
446
+ }
447
+ // Init loading
448
+ const promises = [];
449
+ // Get all images
450
+ let images = [];
451
+ config.elements.forEach((element) => {
452
+ images = images.concat(Array.from(element.getElementsByTagName('img')));
453
+ });
454
+ // Prepare images
455
+ for (const image of images) {
456
+ const src = image.getAttribute('src');
457
+ // Check if changes are necessary and possible
458
+ if ((image.complete && image.naturalHeight !== 0) || // already laoded
459
+ image.getAttribute('loading') !== 'lazy' || // loading attribute !== lazy
460
+ !src || // Image has no src
461
+ ((_a = config.regExp) === null || _a === void 0 ? void 0 : _a.test(src)) // Test if src matches regular expression
462
+ ) {
463
+ continue;
464
+ }
465
+ // Replace lazy loading image with immediately loading image
466
+ const newImage = document.createElement('img');
467
+ Array.from(image.attributes).forEach((attr) => {
468
+ if (attr.name !== 'loading' && attr.name !== 'src') {
469
+ newImage.setAttribute(attr.name, attr.value);
470
+ }
471
+ });
472
+ image.replaceWith(newImage);
473
+ // Wait for laoding
474
+ promises.push(this.loadImage(newImage, src));
475
+ }
476
+ if (config.timeout) {
477
+ timeoutId = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
478
+ resolved = true;
479
+ if (config.doneFunnction) {
480
+ yield config.doneFunction();
481
+ }
482
+ resolve(undefined);
483
+ }));
484
+ }
485
+ // Wait until all images loaded
486
+ Promise.all(promises)
487
+ .catch((e) => console.error('Error during image loading', e))
488
+ .finally(() => __awaiter(this, void 0, void 0, function* () {
489
+ // Check if already resolved
490
+ if (resolved) {
491
+ return;
492
+ }
493
+ // Stop timeout
494
+ if (timeoutId) {
495
+ clearTimeout(timeoutId);
496
+ }
497
+ // Return result
498
+ let result;
499
+ if (config.doneFunnction) {
500
+ result = yield config.doneFunction();
501
+ }
502
+ resolve(result);
503
+ }));
504
+ });
505
+ }
506
+ /**
507
+ * Load image for image element
508
+ *
509
+ * @param imageElement
510
+ * @param src
511
+ * @protected
512
+ */
513
+ loadImage(imageElement, src) {
514
+ return __awaiter(this, void 0, void 0, function* () {
515
+ src = src || imageElement.src;
516
+ return new Promise((resolve, reject) => {
517
+ imageElement.onload = () => resolve(imageElement);
518
+ imageElement.onerror = reject;
519
+ imageElement.src = src;
520
+ });
521
+ });
522
+ }
523
+ }
524
+ ImageService.ɵprov = i0.ɵɵdefineInjectable({ factory: function ImageService_Factory() { return new ImageService(); }, token: ImageService, providedIn: "root" });
525
+ ImageService.decorators = [
526
+ { type: Injectable, args: [{
527
+ providedIn: 'root',
528
+ },] }
529
+ ];
530
+
420
531
  /**
421
532
  * Set same height (maximum) to different elements
422
533
  */
423
534
  class MatchHeightDirective {
424
- constructor(el) {
535
+ constructor(el, imageService, zone) {
425
536
  this.el = el;
537
+ this.imageService = imageService;
538
+ this.zone = zone;
539
+ this.loadLazyImages = true;
426
540
  }
427
541
  onResize() {
428
542
  this.matchHeight(this.el.nativeElement, this.ltMatchHeight);
@@ -431,33 +545,41 @@ class MatchHeightDirective {
431
545
  this.matchHeight(this.el.nativeElement, this.ltMatchHeight);
432
546
  }
433
547
  matchHeight(parent, target) {
434
- if (!parent) {
435
- return;
436
- }
437
- let classNames;
438
- if (!Array.isArray(target)) {
439
- classNames = [target];
440
- }
441
- else {
442
- classNames = target;
443
- }
444
- classNames.forEach((className) => {
445
- // find all child structureElements with the selected class name
446
- const children = parent.getElementsByClassName(className);
447
- if (!children) {
548
+ // Timeout is set to force Angular to wait for all actions and only continue once the process has run through.
549
+ // Otherwise, an infinite loop will occur.
550
+ setTimeout(() => __awaiter(this, void 0, void 0, function* () {
551
+ if (!parent) {
448
552
  return;
449
553
  }
450
- // reset all children height
451
- Array.from(children).forEach((x) => {
452
- x.style.height = 'initial';
453
- });
454
- // get all the child structureElements heights
455
- const itemHeights = Array.from(children).map((x) => x.getBoundingClientRect().height);
456
- // find out the tallest one
457
- const maxHeight = itemHeights.reduce((prev, curr) => (curr > prev ? curr : prev), 0);
458
- // update the child structureElements height to the tallest one
459
- Array.from(children).forEach((x) => (x.style.height = `${maxHeight}px`));
460
- });
554
+ let classNames;
555
+ if (!Array.isArray(target)) {
556
+ classNames = [target];
557
+ }
558
+ else {
559
+ classNames = target;
560
+ }
561
+ for (const className of classNames) {
562
+ // find all child structureElements with the selected class name
563
+ const children = parent.getElementsByClassName(className);
564
+ if (!children || children.length < 2) {
565
+ continue;
566
+ }
567
+ // Load images of children
568
+ if (this.loadLazyImages) {
569
+ yield this.imageService.preLoadImages({ elements: children });
570
+ }
571
+ // reset all children height
572
+ Array.from(children).forEach((element) => {
573
+ element.style.height = 'initial';
574
+ });
575
+ // get all the child structureElements heights
576
+ const itemHeights = Array.from(children).map((x) => x.getBoundingClientRect().height);
577
+ // find out the tallest one
578
+ const maxHeight = itemHeights.reduce((prev, curr) => (curr > prev ? curr : prev), 0);
579
+ // update the child structureElements height to the tallest one
580
+ Array.from(children).forEach((element) => (element.style.height = `${maxHeight}px`));
581
+ }
582
+ }), 1);
461
583
  }
462
584
  }
463
585
  MatchHeightDirective.decorators = [
@@ -466,10 +588,13 @@ MatchHeightDirective.decorators = [
466
588
  },] }
467
589
  ];
468
590
  MatchHeightDirective.ctorParameters = () => [
469
- { type: ElementRef }
591
+ { type: ElementRef },
592
+ { type: ImageService },
593
+ { type: NgZone }
470
594
  ];
471
595
  MatchHeightDirective.propDecorators = {
472
596
  ltMatchHeight: [{ type: Input }],
597
+ loadLazyImages: [{ type: Input }],
473
598
  onResize: [{ type: HostListener, args: ['window:resize',] }]
474
599
  };
475
600
 
@@ -2000,35 +2125,78 @@ class ScrollService {
2000
2125
  /**
2001
2126
  * Initializer
2002
2127
  */
2003
- constructor(router, moduleConfig) {
2128
+ constructor(router, moduleConfig, imageService) {
2004
2129
  this.router = router;
2005
2130
  this.moduleConfig = moduleConfig;
2131
+ this.imageService = imageService;
2006
2132
  /**
2007
- * Scroll offset
2133
+ * Is a scroll listener set
2134
+ *
2135
+ * @protected
2008
2136
  */
2009
- this.scrollOffset = 100;
2137
+ this.scrollListener = false;
2010
2138
  /**
2011
- * Detection offset
2012
- * Should be >= scrollOffset
2139
+ * Current scroll timer;
2140
+ *
2141
+ * @protected
2013
2142
  */
2014
- this.detectionOffset = 200;
2015
- const config = Object.assign({ scrollDetectionOffset: 200, scrollOffset: 100 }, moduleConfig);
2143
+ this.scrollTimer = -1;
2144
+ const config = Object.assign({ scrollDetectionOffset: 200, scrollOffset: 100, scrollSamplingRate: 100 }, moduleConfig);
2016
2145
  this.detectionOffset = config.scrollDetectionOffset;
2017
2146
  this.scrollOffset = config.scrollOffset;
2147
+ this.scrollSamplingRate = config.scrollSamplingRate;
2018
2148
  }
2019
2149
  /**
2020
2150
  * Scroll to structureElement with specific ID
2021
2151
  */
2022
2152
  scrollTo(id, options = {}) {
2023
- options = Object.assign({ stopPropagation: true }, options);
2024
- if (this.router.url !== '/' && !this.router.url.startsWith('/#')) {
2025
- this.router.navigate(['/'], { fragment: id });
2026
- return options.stopPropagation ? false : true;
2027
- }
2028
- const el = document.getElementById(id);
2029
- const cords = el.getBoundingClientRect();
2030
- window.scrollBy({ top: cords.top - this.scrollOffset, behavior: 'smooth' });
2031
- return options.stopPropagation ? false : true;
2153
+ return __awaiter(this, void 0, void 0, function* () {
2154
+ // Prepare config
2155
+ const config = Object.assign({ checkUrl: true, loadImagesBefore: true, resetStyles: false, setOverflow: true, stopPropagation: true }, options);
2156
+ // Check current URL
2157
+ if (config.checkUrl && this.router.url !== '/' && !this.router.url.startsWith('/#')) {
2158
+ yield this.router.navigate(['/'], { fragment: id });
2159
+ return !config.stopPropagation;
2160
+ }
2161
+ // Check if target element exits
2162
+ const el = document.getElementById(id);
2163
+ if (!el) {
2164
+ console.error('Missing scroll element with ID:', id);
2165
+ return !config.stopPropagation;
2166
+ }
2167
+ // Check if body overflow is visible
2168
+ // This is necessary for scroll handling
2169
+ const body = document.querySelector('body');
2170
+ let undoOverflowChanges = false;
2171
+ const scrollBodyStyles = JSON.parse(JSON.stringify(window.getComputedStyle(body)));
2172
+ if (config.setOverflow && (scrollBodyStyles.overflowX !== 'visible' || scrollBodyStyles.overflowY !== 'visible')) {
2173
+ // Set body overflow to visible
2174
+ console.warn('Style of body element for overflowX and overflowY will be set to "visible" for scrolling!');
2175
+ body.style.overflowX = 'visible';
2176
+ body.style.overflowY = 'visible';
2177
+ undoOverflowChanges = true;
2178
+ }
2179
+ // Load images before scrolling (once)
2180
+ if (config.loadImagesBefore) {
2181
+ yield this.imageService.preLoadImages();
2182
+ }
2183
+ // Get get bounding rect of target element
2184
+ const cords = el.getBoundingClientRect();
2185
+ // Scroll to element
2186
+ window.scrollBy({ top: cords.top - this.scrollOffset, behavior: 'smooth' });
2187
+ yield this.scrollDone({
2188
+ doneFunction: () => {
2189
+ // Undo changes
2190
+ if (undoOverflowChanges && config.resetStyles) {
2191
+ body.style.overflowX = scrollBodyStyles.overflowX;
2192
+ body.style.overflowY = scrollBodyStyles.overflowY;
2193
+ console.warn('Original style of body element for overflowX and overflowY is set.');
2194
+ }
2195
+ },
2196
+ });
2197
+ // Return stop propagation configuration
2198
+ return !config.stopPropagation;
2199
+ });
2032
2200
  }
2033
2201
  /**
2034
2202
  * Get last active structureElement
@@ -2058,8 +2226,71 @@ class ScrollService {
2058
2226
  const lastActiveElement = this.getLastActiveElement(list);
2059
2227
  return id === lastActiveElement || (lastActiveElement === null && isFirst);
2060
2228
  }
2229
+ /**
2230
+ * Scroll done promise
2231
+ */
2232
+ scrollDone(options = {}) {
2233
+ // Init configuration
2234
+ const config = Object.assign({ doneFunction: undefined, timeout: undefined }, options);
2235
+ // Return promise for scroll done
2236
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
2237
+ // Init variables
2238
+ let rejected = false;
2239
+ let timeoutId;
2240
+ // Set timeout handling if necessary
2241
+ if (config.timeout) {
2242
+ timeoutId = window.setTimeout(() => {
2243
+ rejected = true;
2244
+ reject(new Error('Timeout for scrollDone after:' + config.timeout));
2245
+ }, config.timeout);
2246
+ }
2247
+ // Wait for scroll done
2248
+ yield this.scrollDoneDetection();
2249
+ // Resolve promise if not already rejected
2250
+ if (!rejected) {
2251
+ // Stop timeout if set
2252
+ if (timeoutId) {
2253
+ window.clearTimeout(timeoutId);
2254
+ }
2255
+ // Get result (if doneFunction is set)
2256
+ let result;
2257
+ if (config.doneFunction) {
2258
+ result = yield config.doneFunction();
2259
+ }
2260
+ // Scroll done
2261
+ resolve(result);
2262
+ }
2263
+ }));
2264
+ }
2265
+ /**
2266
+ * Scroll done detecton
2267
+ *
2268
+ * @protected
2269
+ */
2270
+ scrollDoneDetection() {
2271
+ // Return current scroll promise (Singelton)
2272
+ if (this.scrollPromise) {
2273
+ return this.scrollPromise;
2274
+ }
2275
+ // Create current scroll promise
2276
+ this.scrollPromise = new Promise((resolve) => {
2277
+ this.scrollPromiseResolve = resolve;
2278
+ if (this.scrollTimer !== -1) {
2279
+ window.clearTimeout(this.scrollTimer);
2280
+ }
2281
+ this.scrollTimer = window.setTimeout(() => {
2282
+ window.removeEventListener('scroll', this.scrollDoneDetection.bind(this));
2283
+ this.scrollPromise = undefined;
2284
+ resolve();
2285
+ }, this.scrollSamplingRate);
2286
+ });
2287
+ // Lisitening to scroll event;
2288
+ window.addEventListener('scroll', this.scrollDoneDetection.bind(this));
2289
+ // Return promise
2290
+ return this.scrollPromise;
2291
+ }
2061
2292
  }
2062
- 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" });
2293
+ ScrollService.ɵprov = i0.ɵɵdefineInjectable({ factory: function ScrollService_Factory() { return new ScrollService(i0.ɵɵinject(i1$1.Router), i0.ɵɵinject(BASE_MODULE_CONFIG), i0.ɵɵinject(ImageService)); }, token: ScrollService, providedIn: "root" });
2063
2294
  ScrollService.decorators = [
2064
2295
  { type: Injectable, args: [{
2065
2296
  providedIn: 'root',
@@ -2067,7 +2298,8 @@ ScrollService.decorators = [
2067
2298
  ];
2068
2299
  ScrollService.ctorParameters = () => [
2069
2300
  { type: Router },
2070
- { type: undefined, decorators: [{ type: Inject, args: [BASE_MODULE_CONFIG,] }] }
2301
+ { type: undefined, decorators: [{ type: Inject, args: [BASE_MODULE_CONFIG,] }] },
2302
+ { type: ImageService }
2071
2303
  ];
2072
2304
 
2073
2305
  /**
@@ -2140,5 +2372,5 @@ function MustMatch(controlName, matchingControlName) {
2140
2372
  * Generated bundle index. Do not edit.
2141
2373
  */
2142
2374
 
2143
- export { AuthGuard, AuthService, BASE_MODULE_CONFIG, BaseModule, BasicUser, CanDeactivateGuard, ComparisonOperatorEnum, ContextMenuDirective, EllipsesPipe, FullscreenService, GraphQLEnum, GraphQLMeta, GraphQLMetaService, GraphQLPlusService, GraphQLRequestType, GraphQLService, GraphQLType, Helper, LazyLoadDirective, LoaderService, LogicalOperatorEnum, LogoutGuard, MatchHeightDirective, MustMatch, ResizableDirective, SEOService, SafeHtmlPipe, ScrollService, SortOrderEnum, Standard, StorageService, apolloOptionsFactory };
2375
+ export { AuthGuard, AuthService, BASE_MODULE_CONFIG, BaseModule, BasicUser, CanDeactivateGuard, ComparisonOperatorEnum, ContextMenuDirective, EllipsesPipe, FullscreenService, GraphQLEnum, GraphQLMeta, GraphQLMetaService, GraphQLPlusService, GraphQLRequestType, GraphQLService, GraphQLType, Helper, ImageService, LazyLoadDirective, LoaderService, LogicalOperatorEnum, LogoutGuard, MatchHeightDirective, MustMatch, ResizableDirective, SEOService, SafeHtmlPipe, ScrollService, SortOrderEnum, Standard, StorageService, apolloOptionsFactory };
2144
2376
  //# sourceMappingURL=lenne.tech-ng-base-src-lib-base.js.map