@lenne.tech/ng-base 0.2.1 → 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.
@@ -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
 
@@ -2004,31 +2005,73 @@ class ScrollService {
2004
2005
  this.router = router;
2005
2006
  this.moduleConfig = moduleConfig;
2006
2007
  /**
2007
- * Scroll offset
2008
+ * Is a scroll listener set
2009
+ *
2010
+ * @protected
2008
2011
  */
2009
- this.scrollOffset = 100;
2012
+ this.scrollListener = false;
2010
2013
  /**
2011
- * Detection offset
2012
- * Should be >= scrollOffset
2014
+ * Current scroll timer;
2015
+ *
2016
+ * @protected
2013
2017
  */
2014
- this.detectionOffset = 200;
2015
- 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);
2016
2020
  this.detectionOffset = config.scrollDetectionOffset;
2017
2021
  this.scrollOffset = config.scrollOffset;
2022
+ this.scrollSamplingRate = config.scrollSamplingRate;
2018
2023
  }
2019
2024
  /**
2020
2025
  * Scroll to structureElement with specific ID
2021
2026
  */
2022
2027
  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;
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
+ });
2032
2075
  }
2033
2076
  /**
2034
2077
  * Get last active structureElement
@@ -2058,6 +2101,148 @@ class ScrollService {
2058
2101
  const lastActiveElement = this.getLastActiveElement(list);
2059
2102
  return id === lastActiveElement || (lastActiveElement === null && isFirst);
2060
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
+ }
2061
2246
  }
2062
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" });
2063
2248
  ScrollService.decorators = [