inviton-powerduck 0.0.71 → 0.0.73

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.
@@ -10,368 +10,306 @@ import { isNullOrEmpty } from "./utils/is-null-or-empty";
10
10
  import NotificationProvider from './../components/ui/notification';
11
11
  import PowerduckState from "../app/powerduck-state";
12
12
  import { IValidation, ValidationState } from './static-wrappers/interfaces/validation-interface';
13
+ import ScrollUtils from "./scroll-utils";
13
14
 
14
15
  export abstract class PowerduckViewModelBase extends Vue {
15
- blockRoot: boolean = true;
16
- authorized: boolean = true;
17
- v$: Validation;
16
+ blockRoot: boolean = true;
17
+ authorized: boolean = true;
18
+ v$: Validation;
18
19
 
19
- constructor(optionBuilder: OptionBuilder, vueInstance: any) {
20
- super(optionBuilder, vueInstance);
21
- this.v$ = useVuelidate({
20
+ constructor(optionBuilder: OptionBuilder, vueInstance: any) {
21
+ super(optionBuilder, vueInstance);
22
+ this.v$ = useVuelidate({
22
23
  $scope: vueInstance
23
24
  }) as any as Validation;
24
- }
25
-
26
- /**
27
- * Current interface language
28
- */
29
- get appLanguage(): Language {
30
- return PowerduckState.getCurrentLanguage();
31
- }
32
-
33
- /**
34
- * Try GET data from Inviton API enpoint
35
- */
36
- public async tryGetDataByArgs<TData, TArgs = {}>(args: TryCallApiArgs<TData, TArgs>): Promise<TData> {
37
- return this.tryCallApiByArgs(args, false);
38
- }
39
-
40
- /**
41
- * Try POST data to Inviton API endpoint
42
- */
43
- public async tryPostDataByArgs<TData, TArgs>(args: TryCallApiArgs<TData, TArgs>): Promise<TryPostApiResponse<TData>> {
44
- var retVal = await this.tryCallApiByArgs(args, true);
45
- if (retVal != null && retVal["ajaxErr"] != null) {
46
- return {
47
- data: null as any,
48
- error: retVal["ajaxErr"],
49
- result: TryCallApiResult.Error,
50
- };
51
- } else {
52
- return {
53
- data: retVal,
54
- error: null as any,
55
- result: TryCallApiResult.Success,
56
- };
57
- }
58
- }
59
-
60
- /**
61
- * Try PATCH data to Inviton API endpoint
62
- * just decorator
63
- */
64
- public async tryPatchDataByArgs<TData, TArgs>(args: TryCallApiArgs<TData, TArgs>): Promise<TryPatchApiResponse<TData>> {
65
- return await this.tryPostDataByArgs(args);
66
- }
67
-
68
- public async tryDeleteDataByArgs<TData, TArgs>(args: TryCallApiArgs<TData, TArgs>): Promise<TData> {
69
- return this.tryCallApiByArgs(args, false);
70
- }
71
-
72
- private async tryCallApiByArgs<TData, TArgs>(args: TryCallApiArgs<TData, TArgs>, includeError: boolean): Promise<TData> {
73
- var retVal: TData;
74
- let handle: any = null;
75
-
76
- if (args.blockRoot != false) {
77
- handle = setTimeout(() => {
78
- this.blockRoot = true;
79
- }, 850);
80
- }
81
-
82
- var apiMethod = args.apiMethod as any;
83
- var promise = apiMethod(args.requestArgs, args.timeout);
84
-
85
- try {
86
- retVal = await promise;
87
- } catch (e: any) {
88
- let err: AjaxError = e;
89
-
90
- if (args.blockRoot != false) {
91
- this.blockRoot = false;
92
- }
93
-
94
- if (e == "not authorized, token expired") {
95
- err = {
96
- authorized: false,
97
- responseText: PowerduckState.getResourceValue('loginExpired'),
98
- } as any;
99
- }
100
-
101
- if (err.authorized == false || e == "not authorized, token expired") {
102
- (window as any).loginModalRootInstance.show();
103
- }
104
-
105
- if (!err.authorized && args.toggleAuthorization) {
106
- this.authorized = err.authorized;
107
- }
108
-
109
- if (args.showError != false) {
110
- let parsedMsg = PowerduckState.parseErrorMessage(err.responseText);
111
- if (!isNullOrEmpty(parsedMsg)) {
112
- this.showErrorMessage(parsedMsg);
113
- } else if (err.responseText) {
114
- this.showErrorMessage(err.responseText);
115
- } else if ((err as any).message) {
116
- this.showErrorMessage((err as any).message);
117
- }
118
- }
119
-
120
- if (includeError) {
121
- retVal = {
122
- ajaxErr: err,
123
- } as any;
124
- } else {
125
- retVal = null as any;
126
- }
127
- }
128
-
129
- if (args.blockRoot != false) {
130
- try {
131
- clearTimeout(handle);
132
- } catch (error) { }
133
-
134
- if (this.blockRoot == true) {
135
- this.blockRoot = false;
136
- }
137
- }
138
-
139
- return retVal;
140
- }
141
-
142
- /**
143
- * Get children components of given type
144
- *
145
- * @param typeName Name of the type
146
- */
147
- getChildrenByType<T>(typeName: string): Array<T> {
148
- return PortalUtils.getChildrenByType(this, typeName);
149
- }
150
-
151
- /**
152
- * Displays unobtrusive error message
153
- * @param errMsg Error message
154
- */
155
- showErrorMessage(errMsg: string): void {
156
- NotificationProvider.showErrorMessage(errMsg);
157
- }
158
-
159
- /**
160
- * Displays unobtrusive success message
161
- * @param successMsg Success message
162
- */
163
- showSuccessMessage(successMsg: string): void {
164
- NotificationProvider.showSuccessMessage(successMsg);
165
- }
166
-
167
- /**
168
- * Parse variable into number
169
- * @param val
170
- * @param defaultValue
171
- */
172
- getNumericValue(val: any, defaultValue?: number) {
173
- if (val != null) {
174
- try {
175
- val = Number(val);
176
- if (isNaN(val)) {
177
- val = defaultValue;
178
- }
179
- } catch (e) {
180
- val = defaultValue;
181
- }
182
- } else {
183
- val = defaultValue;
184
- }
185
-
186
- return val;
187
- }
188
-
189
- /**
190
- * Validates current viewModel state based on given valdiation ruleset
191
- */
192
- async validate(showErrorMessage?: boolean, silent?: boolean): Promise<boolean> {
193
- if (localStorage.getItem("disableValidation") == "1") {
194
- return true;
195
- }
196
-
197
- if (this.v$ == null) {
198
- throw "Validation rules not specified, has to be specified in @Component declaration!";
199
- }
200
-
201
- var isInvalid = !(await this.v$.$validate());
202
- if (isInvalid) {
203
- if (showErrorMessage != false) {
204
- this.showValidationErrorMessage();
205
- }
206
-
207
- if (silent != true) {
208
- this.validationIncludeDirty = true;
209
- this.$nextTick(() => {
210
- this.scrollToFirstPossibleError($(this.unwrapRootElement()));
211
- });
212
- }
213
- }
214
-
215
- return !isInvalid;
216
- }
217
-
218
- unwrapRootElement(): HTMLElement {
219
- if ((this.$el as any).$getChildSlots != null) {
220
- return (((this.$el as any).$getChildSlots() || [])[0]?.el || this.$el) as any;
221
- }
222
-
223
- return this.$el as any;
224
- }
225
-
226
- showValidationErrorMessage() {
227
- this.showErrorMessage(PowerduckState.getResourceValue('errorsOnForm'));
228
- }
229
-
230
- scrollToFirstPossibleError(context: JQuery) {
231
- setTimeout(() => {
232
- var scrollContext = context.find(".form-group.has-danger, .input-group.has-danger").first();
233
- if (scrollContext.length == 0) {
234
- scrollContext = $(".modal.show .form-group.has-danger, .modal.show .input-group.has-danger").first();
235
- }
236
-
237
- if (scrollContext.length > 0) {
238
- //Prevents multiple scrolling in one run
239
- let lastScroll = (PowerduckState as any)._lastValidationScroll || 0;
240
- let now = new Date().getTime();
241
- if (now - lastScroll < 800) {
242
- return;
243
- } else {
244
- (PowerduckState as any)._lastValidationScroll = now;
245
- }
246
-
247
- this.scrollToElem(scrollContext.first()[0]);
248
- }
249
- }, 10);
250
- }
251
-
252
- /**
253
- * Scrolls to element
254
- * @param elem
255
- */
256
- scrollToElem(elem: typeof Vue | Element | typeof Vue[] | Element[], mobileOffset?: boolean | number, mobileOffsetSmoothing?: boolean, animated?: boolean, instant?: boolean): void {
257
- if (elem == null) {
258
- return;
259
- }
260
-
261
- let offset = 0;
262
- let otherHeaderHeight = $("nav.navbar.fixed-top").height();
263
- if (otherHeaderHeight == null) {
264
- otherHeaderHeight = $("header").height();
265
- }
266
-
267
- if (($(window).width() as number < 768 && mobileOffset != false) || otherHeaderHeight > 0) {
268
- if ((mobileOffset as number) > 1) {
269
- offset = mobileOffset as number;
270
- } else {
271
- offset = $(".topnavbar-wrap").height();
272
- if (offset == 0 || offset == null || isNaN(offset)) {
273
- offset = otherHeaderHeight;
274
- }
275
- }
276
- }
277
-
278
- let itemTop = $(elem).first().offset()?.top;
279
- let modalParent = $(elem as HTMLElement).closest(".modal");
280
- if (modalParent.length == 0) {
281
- modalParent = null;
282
- } else {
283
- itemTop = modalParent.scrollTop() + itemTop - 95;
284
- }
285
-
286
- if (isNaN(offset)) {
287
- offset = 0;
288
- }
289
-
290
- this.scrollToPos(itemTop - offset, modalParent, animated, instant);
291
- }
292
-
293
- /**
294
- * Scrolls to position
295
- * @param elem
296
- */
297
- scrollToPos(position: number, context?: JQuery, animated?: boolean, instant?: boolean): void {
298
- if (instant != true) {
299
- setTimeout(() => {
300
- (context?.length > 0 ? context[0] : window).scrollTo({
301
- top: position,
302
- behavior: (animated != false ? 'smooth' : 'instant') as any
303
- });
304
- });
25
+ }
26
+
27
+ /**
28
+ * Current interface language
29
+ */
30
+ get appLanguage(): Language {
31
+ return PowerduckState.getCurrentLanguage();
32
+ }
33
+
34
+ /**
35
+ * Try GET data from Inviton API enpoint
36
+ */
37
+ public async tryGetDataByArgs<TData, TArgs = {}>(args: TryCallApiArgs<TData, TArgs>): Promise<TData> {
38
+ return this.tryCallApiByArgs(args, false);
39
+ }
40
+
41
+ /**
42
+ * Try POST data to Inviton API endpoint
43
+ */
44
+ public async tryPostDataByArgs<TData, TArgs>(args: TryCallApiArgs<TData, TArgs>): Promise<TryPostApiResponse<TData>> {
45
+ var retVal = await this.tryCallApiByArgs(args, true);
46
+ if (retVal != null && retVal["ajaxErr"] != null) {
47
+ return {
48
+ data: null as any,
49
+ error: retVal["ajaxErr"],
50
+ result: TryCallApiResult.Error,
51
+ };
305
52
  } else {
306
- (context?.length > 0 ? context[0] : window).scrollTo({
307
- top: position,
308
- behavior: (animated != false ? 'smooth' : 'instant') as any
309
- });
53
+ return {
54
+ data: retVal,
55
+ error: null as any,
56
+ result: TryCallApiResult.Success,
57
+ };
310
58
  }
311
- }
312
-
313
- /**
314
- * Determines if DIRTY should be included in validation
315
- */
316
- validationIncludeDirty: boolean = false;
317
-
318
- /**
319
- * Obtains validation state of given property
320
- * @param valProp Validation property
321
- */
322
- validationStateOf(valProp: IValidation | IValidation[], customMessage?: string): ValidationState {
323
- let retVal: ValidationState = null as any;
324
- if (!PortalUtils.isArray(valProp)) {
325
- retVal = ValidationHelper.getValidationDisplayState(valProp as any, this.validationIncludeDirty);
326
- } else {
327
- let validationResult: ValidationState;
328
- for (let i = 0, len = (valProp as IValidation[]).length; i < len; i++) {
329
- validationResult = ValidationHelper.getValidationDisplayState(valProp[i], this.validationIncludeDirty);
330
- if (!validationResult.valid) {
331
- retVal = validationResult;
332
- break;
333
- }
334
- }
335
- }
336
-
337
- if (retVal?.valid == false) {
338
- if (!isNullOrEmpty(customMessage as any)) {
339
- retVal = retVal || {} as any;
340
- retVal.errorMessage = customMessage as string;
341
- }
342
- }
343
-
344
- return retVal;
345
- }
346
-
347
- /**
348
- * Resets validation state of the viewModel
349
- */
350
- resetValidation() {
351
- ValidationHelper.resetValidation(this);
352
- this.validationIncludeDirty = false;
353
- }
59
+ }
60
+
61
+ /**
62
+ * Try PATCH data to Inviton API endpoint
63
+ * just decorator
64
+ */
65
+ public async tryPatchDataByArgs<TData, TArgs>(args: TryCallApiArgs<TData, TArgs>): Promise<TryPatchApiResponse<TData>> {
66
+ return await this.tryPostDataByArgs(args);
67
+ }
68
+
69
+ public async tryDeleteDataByArgs<TData, TArgs>(args: TryCallApiArgs<TData, TArgs>): Promise<TData> {
70
+ return this.tryCallApiByArgs(args, false);
71
+ }
72
+
73
+ private async tryCallApiByArgs<TData, TArgs>(args: TryCallApiArgs<TData, TArgs>, includeError: boolean): Promise<TData> {
74
+ var retVal: TData;
75
+ let handle: any = null;
76
+
77
+ if (args.blockRoot != false) {
78
+ handle = setTimeout(() => {
79
+ this.blockRoot = true;
80
+ }, 850);
81
+ }
82
+
83
+ var apiMethod = args.apiMethod as any;
84
+ var promise = apiMethod(args.requestArgs, args.timeout);
85
+
86
+ try {
87
+ retVal = await promise;
88
+ } catch (e: any) {
89
+ let err: AjaxError = e;
90
+
91
+ if (args.blockRoot != false) {
92
+ this.blockRoot = false;
93
+ }
94
+
95
+ if (e == "not authorized, token expired") {
96
+ err = {
97
+ authorized: false,
98
+ responseText: PowerduckState.getResourceValue('loginExpired'),
99
+ } as any;
100
+ }
101
+
102
+ if (err.authorized == false || e == "not authorized, token expired") {
103
+ (window as any).loginModalRootInstance.show();
104
+ }
105
+
106
+ if (!err.authorized && args.toggleAuthorization) {
107
+ this.authorized = err.authorized;
108
+ }
109
+
110
+ if (args.showError != false) {
111
+ let parsedMsg = PowerduckState.parseErrorMessage(err.responseText);
112
+ if (!isNullOrEmpty(parsedMsg)) {
113
+ this.showErrorMessage(parsedMsg);
114
+ } else if (err.responseText) {
115
+ this.showErrorMessage(err.responseText);
116
+ } else if ((err as any).message) {
117
+ this.showErrorMessage((err as any).message);
118
+ }
119
+ }
120
+
121
+ if (includeError) {
122
+ retVal = {
123
+ ajaxErr: err,
124
+ } as any;
125
+ } else {
126
+ retVal = null as any;
127
+ }
128
+ }
129
+
130
+ if (args.blockRoot != false) {
131
+ try {
132
+ clearTimeout(handle);
133
+ } catch (error) { }
134
+
135
+ if (this.blockRoot == true) {
136
+ this.blockRoot = false;
137
+ }
138
+ }
139
+
140
+ return retVal;
141
+ }
142
+
143
+ /**
144
+ * Get children components of given type
145
+ *
146
+ * @param typeName Name of the type
147
+ */
148
+ getChildrenByType<T>(typeName: string): Array<T> {
149
+ return PortalUtils.getChildrenByType(this, typeName);
150
+ }
151
+
152
+ /**
153
+ * Displays unobtrusive error message
154
+ * @param errMsg Error message
155
+ */
156
+ showErrorMessage(errMsg: string): void {
157
+ NotificationProvider.showErrorMessage(errMsg);
158
+ }
159
+
160
+ /**
161
+ * Displays unobtrusive success message
162
+ * @param successMsg Success message
163
+ */
164
+ showSuccessMessage(successMsg: string): void {
165
+ NotificationProvider.showSuccessMessage(successMsg);
166
+ }
167
+
168
+ /**
169
+ * Parse variable into number
170
+ * @param val
171
+ * @param defaultValue
172
+ */
173
+ getNumericValue(val: any, defaultValue?: number) {
174
+ if (val != null) {
175
+ try {
176
+ val = Number(val);
177
+ if (isNaN(val)) {
178
+ val = defaultValue;
179
+ }
180
+ } catch (e) {
181
+ val = defaultValue;
182
+ }
183
+ } else {
184
+ val = defaultValue;
185
+ }
186
+
187
+ return val;
188
+ }
189
+
190
+ /**
191
+ * Validates current viewModel state based on given valdiation ruleset
192
+ */
193
+ async validate(showErrorMessage?: boolean, silent?: boolean): Promise<boolean> {
194
+ if (localStorage.getItem("disableValidation") == "1") {
195
+ return true;
196
+ }
197
+
198
+ if (this.v$ == null) {
199
+ throw "Validation rules not specified, has to be specified in @Component declaration!";
200
+ }
201
+
202
+ var isInvalid = !(await this.v$.$validate());
203
+ if (isInvalid) {
204
+ if (showErrorMessage != false) {
205
+ this.showValidationErrorMessage();
206
+ }
207
+
208
+ if (silent != true) {
209
+ this.validationIncludeDirty = true;
210
+ this.$nextTick(() => {
211
+ this.scrollToFirstPossibleError($(this.unwrapRootElement()));
212
+ });
213
+ }
214
+ }
215
+
216
+ return !isInvalid;
217
+ }
218
+
219
+ unwrapRootElement(): HTMLElement {
220
+ if ((this.$el as any).$getChildSlots != null) {
221
+ return (((this.$el as any).$getChildSlots() || [])[0]?.el || this.$el) as any;
222
+ }
223
+
224
+ return this.$el as any;
225
+ }
226
+
227
+ showValidationErrorMessage() {
228
+ this.showErrorMessage(PowerduckState.getResourceValue('errorsOnForm'));
229
+ }
230
+
231
+ scrollToFirstPossibleError(context: JQuery) {
232
+ ScrollUtils.scrollToFirstPossibleError(context);
233
+ }
234
+
235
+ /**
236
+ * Scrolls to element
237
+ * @param elem
238
+ */
239
+ scrollToElem(elem: typeof Vue | Element | typeof Vue[] | Element[], mobileOffset?: boolean | number, mobileOffsetSmoothing?: boolean, animated?: boolean, instant?: boolean): void {
240
+ ScrollUtils.scrollToElem(elem, mobileOffset, mobileOffsetSmoothing, animated, instant);
241
+ }
242
+
243
+ /**
244
+ * Scrolls to position
245
+ * @param elem
246
+ */
247
+ scrollToPos(position: number, context?: JQuery, animated?: boolean, instant?: boolean): void {
248
+ ScrollUtils.scrollToPos(position, context, animated, instant);
249
+ }
250
+
251
+ /**
252
+ * Determines if DIRTY should be included in validation
253
+ */
254
+ validationIncludeDirty: boolean = false;
255
+
256
+ /**
257
+ * Obtains validation state of given property
258
+ * @param valProp Validation property
259
+ */
260
+ validationStateOf(valProp: IValidation | IValidation[], customMessage?: string): ValidationState {
261
+ let retVal: ValidationState = null as any;
262
+ if (!PortalUtils.isArray(valProp)) {
263
+ retVal = ValidationHelper.getValidationDisplayState(valProp as any, this.validationIncludeDirty);
264
+ } else {
265
+ let validationResult: ValidationState;
266
+ for (let i = 0, len = (valProp as IValidation[]).length; i < len; i++) {
267
+ validationResult = ValidationHelper.getValidationDisplayState(valProp[i], this.validationIncludeDirty);
268
+ if (!validationResult.valid) {
269
+ retVal = validationResult;
270
+ break;
271
+ }
272
+ }
273
+ }
274
+
275
+ if (retVal?.valid == false) {
276
+ if (!isNullOrEmpty(customMessage as any)) {
277
+ retVal = retVal || {} as any;
278
+ retVal.errorMessage = customMessage as string;
279
+ }
280
+ }
281
+
282
+ return retVal;
283
+ }
284
+
285
+ /**
286
+ * Resets validation state of the viewModel
287
+ */
288
+ resetValidation() {
289
+ ValidationHelper.resetValidation(this);
290
+ this.validationIncludeDirty = false;
291
+ }
354
292
  }
355
293
 
356
294
  interface TryCallApiArgs<TData, TArgs> {
357
- apiMethod: (data?: TArgs) => Promise<TData>;
358
- timeout?: number;
359
- requestArgs?: TArgs;
360
- showError?: boolean;
361
- blockRoot?: boolean;
362
- toggleAuthorization?: boolean;
363
- toggleAuthorizationOnNullUser?: boolean;
295
+ apiMethod: (data?: TArgs) => Promise<TData>;
296
+ timeout?: number;
297
+ requestArgs?: TArgs;
298
+ showError?: boolean;
299
+ blockRoot?: boolean;
300
+ toggleAuthorization?: boolean;
301
+ toggleAuthorizationOnNullUser?: boolean;
364
302
  }
365
303
 
366
304
  export interface TryPostApiResponse<TData> {
367
- data: TData;
368
- result: TryCallApiResult;
369
- error: AjaxError;
305
+ data: TData;
306
+ result: TryCallApiResult;
307
+ error: AjaxError;
370
308
  }
371
309
 
372
310
  export interface TryPatchApiResponse<TData> extends TryPostApiResponse<TData> { }
373
311
 
374
312
  interface PortalActionMessage {
375
- action: string;
376
- data: any;
313
+ action: string;
314
+ data: any;
377
315
  }
@@ -0,0 +1,44 @@
1
+ export default class KeyboardOpenTracker {
2
+ static bind() {
3
+ if ((window as any)._keyboardOpenTrackerBound == true) {
4
+ return;
5
+ }
6
+
7
+ (window as any)._keyboardOpenTrackerBound = true;
8
+
9
+ const getOrientation = () => {
10
+ return window.screen.width > window.screen.height ? 'landscape' : 'portrait';
11
+ }
12
+
13
+ const getScreenHeight = () => {
14
+ return window.screen.height; // Constant physical screen height
15
+ }
16
+
17
+ let lastOrientation = getOrientation();
18
+ const handleResize = () => {
19
+ const currentOrientation = getOrientation();
20
+ const currentInnerHeight = window.innerHeight;
21
+ const screenHeight = getScreenHeight();
22
+
23
+ // Different thresholds for portrait/landscape
24
+ const THRESHOLD_PORTRAIT = 150;
25
+ const THRESHOLD_LANDSCAPE = 100;
26
+
27
+ const isPortrait = currentOrientation === 'portrait';
28
+ const threshold = isPortrait ? THRESHOLD_PORTRAIT : THRESHOLD_LANDSCAPE;
29
+
30
+ // Reset baseline when orientation changes
31
+ if (currentOrientation !== lastOrientation) {
32
+ lastOrientation = currentOrientation;
33
+ console.log(`[Resize] Orientation changed → ${currentOrientation}`);
34
+ }
35
+
36
+ const heightDiff = screenHeight - currentInnerHeight;
37
+ const isKeyboardOpen = heightDiff > threshold;
38
+ document.body.classList.toggle('powerduck-keyboard-open', isKeyboardOpen);
39
+ }
40
+
41
+ window.addEventListener('resize', handleResize);
42
+ handleResize(); // Run initially
43
+ }
44
+ }
@@ -0,0 +1,87 @@
1
+ import { Vue } from "vue-facing-decorator";
2
+ import PowerduckState from "../app/powerduck-state";
3
+
4
+ export default class ScrollUtils {
5
+ static scrollToFirstPossibleError(context: JQuery) {
6
+ setTimeout(() => {
7
+ var scrollContext = context.find(".form-group.has-danger, .input-group.has-danger").first();
8
+ if (scrollContext.length == 0) {
9
+ scrollContext = $(".modal.show .form-group.has-danger, .modal.show .input-group.has-danger").first();
10
+ }
11
+
12
+ if (scrollContext.length > 0) {
13
+ //Prevents multiple scrolling in one run
14
+ let lastScroll = (PowerduckState as any)._lastValidationScroll || 0;
15
+ let now = new Date().getTime();
16
+ if (now - lastScroll < 800) {
17
+ return;
18
+ } else {
19
+ (PowerduckState as any)._lastValidationScroll = now;
20
+ }
21
+
22
+ this.scrollToElem(scrollContext.first()[0]);
23
+ }
24
+ }, 10);
25
+ }
26
+
27
+ /**
28
+ * Scrolls to element
29
+ * @param elem
30
+ */
31
+ static scrollToElem(elem: typeof Vue | Element | typeof Vue[] | Element[], mobileOffset?: boolean | number, mobileOffsetSmoothing?: boolean, animated?: boolean, instant?: boolean): void {
32
+ if (elem == null) {
33
+ return;
34
+ }
35
+
36
+ let offset = 0;
37
+ let otherHeaderHeight = $("nav.navbar.fixed-top").height();
38
+ if (otherHeaderHeight == null) {
39
+ otherHeaderHeight = $("header").height();
40
+ }
41
+
42
+ if (($(window).width() as number < 768 && mobileOffset != false) || otherHeaderHeight > 0) {
43
+ if ((mobileOffset as number) > 1) {
44
+ offset = mobileOffset as number;
45
+ } else {
46
+ offset = $(".topnavbar-wrap").height();
47
+ if (offset == 0 || offset == null || isNaN(offset)) {
48
+ offset = otherHeaderHeight;
49
+ }
50
+ }
51
+ }
52
+
53
+ let itemTop = $(elem).first().offset()?.top;
54
+ let modalParent = $(elem as HTMLElement).closest(".modal");
55
+ if (modalParent.length == 0) {
56
+ modalParent = null;
57
+ } else {
58
+ itemTop = modalParent.scrollTop() + itemTop - 95;
59
+ }
60
+
61
+ if (isNaN(offset)) {
62
+ offset = 0;
63
+ }
64
+
65
+ this.scrollToPos(itemTop - offset, modalParent, animated, instant);
66
+ }
67
+
68
+ /**
69
+ * Scrolls to position
70
+ * @param elem
71
+ */
72
+ static scrollToPos(position: number, context?: JQuery, animated?: boolean, instant?: boolean): void {
73
+ if (instant != true) {
74
+ setTimeout(() => {
75
+ (context?.length > 0 ? context[0] : window).scrollTo({
76
+ top: position,
77
+ behavior: (animated != false ? 'smooth' : 'instant') as any
78
+ });
79
+ });
80
+ } else {
81
+ (context?.length > 0 ? context[0] : window).scrollTo({
82
+ top: position,
83
+ behavior: (animated != false ? 'smooth' : 'instant') as any
84
+ });
85
+ }
86
+ }
87
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inviton-powerduck",
3
- "version": "0.0.71",
3
+ "version": "0.0.73",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": " vite build && vue-tsc --declaration --emitDeclarationOnly",