@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
@@ -785,12 +785,162 @@
785
785
  src: [{ type: i0.Input }]
786
786
  };
787
787
 
788
+ /**
789
+ * Image service
790
+ */
791
+ var ImageService = /** @class */ (function () {
792
+ function ImageService() {
793
+ }
794
+ /**
795
+ * Preaload images (for lazy handling)
796
+ *
797
+ * @param options
798
+ * @protected
799
+ */
800
+ ImageService.prototype.preLoadImages = function (options) {
801
+ var _this = this;
802
+ // Init configuration
803
+ var config = Object.assign({ doneFunnction: undefined, elements: document, regExp: undefined, timeout: undefined }, options);
804
+ return new Promise(function (resolve) {
805
+ var e_1, _b;
806
+ var _a;
807
+ // Init variables
808
+ var resolved = false;
809
+ var timeoutId;
810
+ // HTMLCollection to array
811
+ if (config.elements instanceof HTMLCollection) {
812
+ config.elements = Array.from(config.elements);
813
+ }
814
+ // Single element to array
815
+ if (!Array.isArray(config.elements)) {
816
+ config.elements = [config.elements];
817
+ }
818
+ // Init loading
819
+ var promises = [];
820
+ // Get all images
821
+ var images = [];
822
+ config.elements.forEach(function (element) {
823
+ images = images.concat(Array.from(element.getElementsByTagName('img')));
824
+ });
825
+ var _loop_1 = function (image) {
826
+ var src = image.getAttribute('src');
827
+ // Check if changes are necessary and possible
828
+ if ((image.complete && image.naturalHeight !== 0) || // already laoded
829
+ image.getAttribute('loading') !== 'lazy' || // loading attribute !== lazy
830
+ !src || // Image has no src
831
+ ((_a = config.regExp) === null || _a === void 0 ? void 0 : _a.test(src)) // Test if src matches regular expression
832
+ ) {
833
+ return "continue";
834
+ }
835
+ // Replace lazy loading image with immediately loading image
836
+ var newImage = document.createElement('img');
837
+ Array.from(image.attributes).forEach(function (attr) {
838
+ if (attr.name !== 'loading' && attr.name !== 'src') {
839
+ newImage.setAttribute(attr.name, attr.value);
840
+ }
841
+ });
842
+ image.replaceWith(newImage);
843
+ // Wait for laoding
844
+ promises.push(_this.loadImage(newImage, src));
845
+ };
846
+ try {
847
+ // Prepare images
848
+ for (var images_1 = __values(images), images_1_1 = images_1.next(); !images_1_1.done; images_1_1 = images_1.next()) {
849
+ var image = images_1_1.value;
850
+ _loop_1(image);
851
+ }
852
+ }
853
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
854
+ finally {
855
+ try {
856
+ if (images_1_1 && !images_1_1.done && (_b = images_1.return)) _b.call(images_1);
857
+ }
858
+ finally { if (e_1) throw e_1.error; }
859
+ }
860
+ if (config.timeout) {
861
+ timeoutId = setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
862
+ return __generator(this, function (_b) {
863
+ switch (_b.label) {
864
+ case 0:
865
+ resolved = true;
866
+ if (!config.doneFunnction) return [3 /*break*/, 2];
867
+ return [4 /*yield*/, config.doneFunction()];
868
+ case 1:
869
+ _b.sent();
870
+ _b.label = 2;
871
+ case 2:
872
+ resolve(undefined);
873
+ return [2 /*return*/];
874
+ }
875
+ });
876
+ }); });
877
+ }
878
+ // Wait until all images loaded
879
+ Promise.all(promises)
880
+ .catch(function (e) { return console.error('Error during image loading', e); })
881
+ .finally(function () { return __awaiter(_this, void 0, void 0, function () {
882
+ var result;
883
+ return __generator(this, function (_b) {
884
+ switch (_b.label) {
885
+ case 0:
886
+ // Check if already resolved
887
+ if (resolved) {
888
+ return [2 /*return*/];
889
+ }
890
+ // Stop timeout
891
+ if (timeoutId) {
892
+ clearTimeout(timeoutId);
893
+ }
894
+ if (!config.doneFunnction) return [3 /*break*/, 2];
895
+ return [4 /*yield*/, config.doneFunction()];
896
+ case 1:
897
+ result = _b.sent();
898
+ _b.label = 2;
899
+ case 2:
900
+ resolve(result);
901
+ return [2 /*return*/];
902
+ }
903
+ });
904
+ }); });
905
+ });
906
+ };
907
+ /**
908
+ * Load image for image element
909
+ *
910
+ * @param imageElement
911
+ * @param src
912
+ * @protected
913
+ */
914
+ ImageService.prototype.loadImage = function (imageElement, src) {
915
+ return __awaiter(this, void 0, void 0, function () {
916
+ return __generator(this, function (_b) {
917
+ src = src || imageElement.src;
918
+ return [2 /*return*/, new Promise(function (resolve, reject) {
919
+ imageElement.onload = function () { return resolve(imageElement); };
920
+ imageElement.onerror = reject;
921
+ imageElement.src = src;
922
+ })];
923
+ });
924
+ });
925
+ };
926
+ return ImageService;
927
+ }());
928
+ ImageService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function ImageService_Factory() { return new ImageService(); }, token: ImageService, providedIn: "root" });
929
+ ImageService.decorators = [
930
+ { type: i0.Injectable, args: [{
931
+ providedIn: 'root',
932
+ },] }
933
+ ];
934
+
788
935
  /**
789
936
  * Set same height (maximum) to different elements
790
937
  */
791
938
  var MatchHeightDirective = /** @class */ (function () {
792
- function MatchHeightDirective(el) {
939
+ function MatchHeightDirective(el, imageService, zone) {
793
940
  this.el = el;
941
+ this.imageService = imageService;
942
+ this.zone = zone;
943
+ this.loadLazyImages = true;
794
944
  }
795
945
  MatchHeightDirective.prototype.onResize = function () {
796
946
  this.matchHeight(this.el.nativeElement, this.ltMatchHeight);
@@ -799,33 +949,82 @@
799
949
  this.matchHeight(this.el.nativeElement, this.ltMatchHeight);
800
950
  };
801
951
  MatchHeightDirective.prototype.matchHeight = function (parent, target) {
802
- if (!parent) {
803
- return;
804
- }
805
- var classNames;
806
- if (!Array.isArray(target)) {
807
- classNames = [target];
808
- }
809
- else {
810
- classNames = target;
811
- }
812
- classNames.forEach(function (className) {
813
- // find all child structureElements with the selected class name
814
- var children = parent.getElementsByClassName(className);
815
- if (!children) {
816
- return;
817
- }
818
- // reset all children height
819
- Array.from(children).forEach(function (x) {
820
- x.style.height = 'initial';
952
+ var _this = this;
953
+ // Timeout is set to force Angular to wait for all actions and only continue once the process has run through.
954
+ // Otherwise, an infinite loop will occur.
955
+ setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
956
+ var classNames, _loop_1, this_1, classNames_1, classNames_1_1, className, e_1_1;
957
+ var e_1, _a;
958
+ return __generator(this, function (_b) {
959
+ switch (_b.label) {
960
+ case 0:
961
+ if (!parent) {
962
+ return [2 /*return*/];
963
+ }
964
+ if (!Array.isArray(target)) {
965
+ classNames = [target];
966
+ }
967
+ else {
968
+ classNames = target;
969
+ }
970
+ _loop_1 = function (className) {
971
+ var children, itemHeights, maxHeight;
972
+ return __generator(this, function (_c) {
973
+ switch (_c.label) {
974
+ case 0:
975
+ children = parent.getElementsByClassName(className);
976
+ if (!children || children.length < 2) {
977
+ return [2 /*return*/, "continue"];
978
+ }
979
+ if (!this_1.loadLazyImages) return [3 /*break*/, 2];
980
+ return [4 /*yield*/, this_1.imageService.preLoadImages({ elements: children })];
981
+ case 1:
982
+ _c.sent();
983
+ _c.label = 2;
984
+ case 2:
985
+ // reset all children height
986
+ Array.from(children).forEach(function (element) {
987
+ element.style.height = 'initial';
988
+ });
989
+ itemHeights = Array.from(children).map(function (x) { return x.getBoundingClientRect().height; });
990
+ maxHeight = itemHeights.reduce(function (prev, curr) { return (curr > prev ? curr : prev); }, 0);
991
+ // update the child structureElements height to the tallest one
992
+ Array.from(children).forEach(function (element) { return (element.style.height = maxHeight + "px"); });
993
+ return [2 /*return*/];
994
+ }
995
+ });
996
+ };
997
+ this_1 = this;
998
+ _b.label = 1;
999
+ case 1:
1000
+ _b.trys.push([1, 6, 7, 8]);
1001
+ classNames_1 = __values(classNames), classNames_1_1 = classNames_1.next();
1002
+ _b.label = 2;
1003
+ case 2:
1004
+ if (!!classNames_1_1.done) return [3 /*break*/, 5];
1005
+ className = classNames_1_1.value;
1006
+ return [5 /*yield**/, _loop_1(className)];
1007
+ case 3:
1008
+ _b.sent();
1009
+ _b.label = 4;
1010
+ case 4:
1011
+ classNames_1_1 = classNames_1.next();
1012
+ return [3 /*break*/, 2];
1013
+ case 5: return [3 /*break*/, 8];
1014
+ case 6:
1015
+ e_1_1 = _b.sent();
1016
+ e_1 = { error: e_1_1 };
1017
+ return [3 /*break*/, 8];
1018
+ case 7:
1019
+ try {
1020
+ if (classNames_1_1 && !classNames_1_1.done && (_a = classNames_1.return)) _a.call(classNames_1);
1021
+ }
1022
+ finally { if (e_1) throw e_1.error; }
1023
+ return [7 /*endfinally*/];
1024
+ case 8: return [2 /*return*/];
1025
+ }
821
1026
  });
822
- // get all the child structureElements heights
823
- var itemHeights = Array.from(children).map(function (x) { return x.getBoundingClientRect().height; });
824
- // find out the tallest one
825
- var maxHeight = itemHeights.reduce(function (prev, curr) { return (curr > prev ? curr : prev); }, 0);
826
- // update the child structureElements height to the tallest one
827
- Array.from(children).forEach(function (x) { return (x.style.height = maxHeight + "px"); });
828
- });
1027
+ }); }, 1);
829
1028
  };
830
1029
  return MatchHeightDirective;
831
1030
  }());
@@ -835,10 +1034,13 @@
835
1034
  },] }
836
1035
  ];
837
1036
  MatchHeightDirective.ctorParameters = function () { return [
838
- { type: i0.ElementRef }
1037
+ { type: i0.ElementRef },
1038
+ { type: ImageService },
1039
+ { type: i0.NgZone }
839
1040
  ]; };
840
1041
  MatchHeightDirective.propDecorators = {
841
1042
  ltMatchHeight: [{ type: i0.Input }],
1043
+ loadLazyImages: [{ type: i0.Input }],
842
1044
  onResize: [{ type: i0.HostListener, args: ['window:resize',] }]
843
1045
  };
844
1046
 
@@ -2602,36 +2804,85 @@
2602
2804
  /**
2603
2805
  * Initializer
2604
2806
  */
2605
- function ScrollService(router, moduleConfig) {
2807
+ function ScrollService(router, moduleConfig, imageService) {
2606
2808
  this.router = router;
2607
2809
  this.moduleConfig = moduleConfig;
2810
+ this.imageService = imageService;
2608
2811
  /**
2609
- * Scroll offset
2812
+ * Is a scroll listener set
2813
+ *
2814
+ * @protected
2610
2815
  */
2611
- this.scrollOffset = 100;
2816
+ this.scrollListener = false;
2612
2817
  /**
2613
- * Detection offset
2614
- * Should be >= scrollOffset
2818
+ * Current scroll timer;
2819
+ *
2820
+ * @protected
2615
2821
  */
2616
- this.detectionOffset = 200;
2617
- var config = Object.assign({ scrollDetectionOffset: 200, scrollOffset: 100 }, moduleConfig);
2822
+ this.scrollTimer = -1;
2823
+ var config = Object.assign({ scrollDetectionOffset: 200, scrollOffset: 100, scrollSamplingRate: 100 }, moduleConfig);
2618
2824
  this.detectionOffset = config.scrollDetectionOffset;
2619
2825
  this.scrollOffset = config.scrollOffset;
2826
+ this.scrollSamplingRate = config.scrollSamplingRate;
2620
2827
  }
2621
2828
  /**
2622
2829
  * Scroll to structureElement with specific ID
2623
2830
  */
2624
2831
  ScrollService.prototype.scrollTo = function (id, options) {
2625
2832
  if (options === void 0) { options = {}; }
2626
- options = Object.assign({ stopPropagation: true }, options);
2627
- if (this.router.url !== '/' && !this.router.url.startsWith('/#')) {
2628
- this.router.navigate(['/'], { fragment: id });
2629
- return options.stopPropagation ? false : true;
2630
- }
2631
- var el = document.getElementById(id);
2632
- var cords = el.getBoundingClientRect();
2633
- window.scrollBy({ top: cords.top - this.scrollOffset, behavior: 'smooth' });
2634
- return options.stopPropagation ? false : true;
2833
+ return __awaiter(this, void 0, void 0, function () {
2834
+ var config, el, body, undoOverflowChanges, scrollBodyStyles, cords;
2835
+ return __generator(this, function (_a) {
2836
+ switch (_a.label) {
2837
+ case 0:
2838
+ config = Object.assign({ checkUrl: true, loadImagesBefore: true, resetStyles: false, setOverflow: true, stopPropagation: true }, options);
2839
+ if (!(config.checkUrl && this.router.url !== '/' && !this.router.url.startsWith('/#'))) return [3 /*break*/, 2];
2840
+ return [4 /*yield*/, this.router.navigate(['/'], { fragment: id })];
2841
+ case 1:
2842
+ _a.sent();
2843
+ return [2 /*return*/, !config.stopPropagation];
2844
+ case 2:
2845
+ el = document.getElementById(id);
2846
+ if (!el) {
2847
+ console.error('Missing scroll element with ID:', id);
2848
+ return [2 /*return*/, !config.stopPropagation];
2849
+ }
2850
+ body = document.querySelector('body');
2851
+ undoOverflowChanges = false;
2852
+ scrollBodyStyles = JSON.parse(JSON.stringify(window.getComputedStyle(body)));
2853
+ if (config.setOverflow && (scrollBodyStyles.overflowX !== 'visible' || scrollBodyStyles.overflowY !== 'visible')) {
2854
+ // Set body overflow to visible
2855
+ console.warn('Style of body element for overflowX and overflowY will be set to "visible" for scrolling!');
2856
+ body.style.overflowX = 'visible';
2857
+ body.style.overflowY = 'visible';
2858
+ undoOverflowChanges = true;
2859
+ }
2860
+ if (!config.loadImagesBefore) return [3 /*break*/, 4];
2861
+ return [4 /*yield*/, this.imageService.preLoadImages()];
2862
+ case 3:
2863
+ _a.sent();
2864
+ _a.label = 4;
2865
+ case 4:
2866
+ cords = el.getBoundingClientRect();
2867
+ // Scroll to element
2868
+ window.scrollBy({ top: cords.top - this.scrollOffset, behavior: 'smooth' });
2869
+ return [4 /*yield*/, this.scrollDone({
2870
+ doneFunction: function () {
2871
+ // Undo changes
2872
+ if (undoOverflowChanges && config.resetStyles) {
2873
+ body.style.overflowX = scrollBodyStyles.overflowX;
2874
+ body.style.overflowY = scrollBodyStyles.overflowY;
2875
+ console.warn('Original style of body element for overflowX and overflowY is set.');
2876
+ }
2877
+ },
2878
+ })];
2879
+ case 5:
2880
+ _a.sent();
2881
+ // Return stop propagation configuration
2882
+ return [2 /*return*/, !config.stopPropagation];
2883
+ }
2884
+ });
2885
+ });
2635
2886
  };
2636
2887
  /**
2637
2888
  * Get last active structureElement
@@ -2662,9 +2913,84 @@
2662
2913
  var lastActiveElement = this.getLastActiveElement(list);
2663
2914
  return id === lastActiveElement || (lastActiveElement === null && isFirst);
2664
2915
  };
2916
+ /**
2917
+ * Scroll done promise
2918
+ */
2919
+ ScrollService.prototype.scrollDone = function (options) {
2920
+ var _this = this;
2921
+ if (options === void 0) { options = {}; }
2922
+ // Init configuration
2923
+ var config = Object.assign({ doneFunction: undefined, timeout: undefined }, options);
2924
+ // Return promise for scroll done
2925
+ return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
2926
+ var rejected, timeoutId, result;
2927
+ return __generator(this, function (_a) {
2928
+ switch (_a.label) {
2929
+ case 0:
2930
+ rejected = false;
2931
+ // Set timeout handling if necessary
2932
+ if (config.timeout) {
2933
+ timeoutId = window.setTimeout(function () {
2934
+ rejected = true;
2935
+ reject(new Error('Timeout for scrollDone after:' + config.timeout));
2936
+ }, config.timeout);
2937
+ }
2938
+ // Wait for scroll done
2939
+ return [4 /*yield*/, this.scrollDoneDetection()];
2940
+ case 1:
2941
+ // Wait for scroll done
2942
+ _a.sent();
2943
+ if (!!rejected) return [3 /*break*/, 4];
2944
+ // Stop timeout if set
2945
+ if (timeoutId) {
2946
+ window.clearTimeout(timeoutId);
2947
+ }
2948
+ result = void 0;
2949
+ if (!config.doneFunction) return [3 /*break*/, 3];
2950
+ return [4 /*yield*/, config.doneFunction()];
2951
+ case 2:
2952
+ result = _a.sent();
2953
+ _a.label = 3;
2954
+ case 3:
2955
+ // Scroll done
2956
+ resolve(result);
2957
+ _a.label = 4;
2958
+ case 4: return [2 /*return*/];
2959
+ }
2960
+ });
2961
+ }); });
2962
+ };
2963
+ /**
2964
+ * Scroll done detecton
2965
+ *
2966
+ * @protected
2967
+ */
2968
+ ScrollService.prototype.scrollDoneDetection = function () {
2969
+ var _this = this;
2970
+ // Return current scroll promise (Singelton)
2971
+ if (this.scrollPromise) {
2972
+ return this.scrollPromise;
2973
+ }
2974
+ // Create current scroll promise
2975
+ this.scrollPromise = new Promise(function (resolve) {
2976
+ _this.scrollPromiseResolve = resolve;
2977
+ if (_this.scrollTimer !== -1) {
2978
+ window.clearTimeout(_this.scrollTimer);
2979
+ }
2980
+ _this.scrollTimer = window.setTimeout(function () {
2981
+ window.removeEventListener('scroll', _this.scrollDoneDetection.bind(_this));
2982
+ _this.scrollPromise = undefined;
2983
+ resolve();
2984
+ }, _this.scrollSamplingRate);
2985
+ });
2986
+ // Lisitening to scroll event;
2987
+ window.addEventListener('scroll', this.scrollDoneDetection.bind(this));
2988
+ // Return promise
2989
+ return this.scrollPromise;
2990
+ };
2665
2991
  return ScrollService;
2666
2992
  }());
2667
- ScrollService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function ScrollService_Factory() { return new ScrollService(i0__namespace.ɵɵinject(i1__namespace$1.Router), i0__namespace.ɵɵinject(BASE_MODULE_CONFIG)); }, token: ScrollService, providedIn: "root" });
2993
+ ScrollService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function ScrollService_Factory() { return new ScrollService(i0__namespace.ɵɵinject(i1__namespace$1.Router), i0__namespace.ɵɵinject(BASE_MODULE_CONFIG), i0__namespace.ɵɵinject(ImageService)); }, token: ScrollService, providedIn: "root" });
2668
2994
  ScrollService.decorators = [
2669
2995
  { type: i0.Injectable, args: [{
2670
2996
  providedIn: 'root',
@@ -2672,7 +2998,8 @@
2672
2998
  ];
2673
2999
  ScrollService.ctorParameters = function () { return [
2674
3000
  { type: i1$2.Router },
2675
- { type: undefined, decorators: [{ type: i0.Inject, args: [BASE_MODULE_CONFIG,] }] }
3001
+ { type: undefined, decorators: [{ type: i0.Inject, args: [BASE_MODULE_CONFIG,] }] },
3002
+ { type: ImageService }
2676
3003
  ]; };
2677
3004
 
2678
3005
  /**
@@ -2762,6 +3089,7 @@
2762
3089
  exports.GraphQLService = GraphQLService;
2763
3090
  exports.GraphQLType = GraphQLType;
2764
3091
  exports.Helper = Helper;
3092
+ exports.ImageService = ImageService;
2765
3093
  exports.LazyLoadDirective = LazyLoadDirective;
2766
3094
  exports.LoaderService = LoaderService;
2767
3095
  exports.LogoutGuard = LogoutGuard;