@etsoo/shared 1.2.45 → 1.2.47

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.
package/README.md CHANGED
@@ -120,6 +120,8 @@ Data type definitions and type safe functions. ListItemType, ListItemType1 and L
120
120
  |DataType|Data type enum|
121
121
  |AddAndEditType|Add and edit data type|
122
122
  |AddOrEditType|Add or edit conditional type|
123
+ |addUrlParam|Add parameter to URL|
124
+ |addUrlParams|Add parameters to URL|
123
125
  |Basic|Basic types, includes number, bigint, Date, boolean, string|
124
126
  |BasicArray|Basic type name array|
125
127
  |BasicConditional|Conditional type based on BasicNames|
@@ -241,8 +243,10 @@ Extend current class/object functioning
241
243
  |---:|---|
242
244
  |applyMixins|Apply mixins to current class|
243
245
  |delayedExecutor|Create delayed executor|
246
+ |intervalFor|Repeat interval for callback|
244
247
  |promiseHandler|Promise handler to catch error|
245
248
  |sleep|Delay promise|
249
+ |waitFor|Wait for condition meets and execute callback|
246
250
 
247
251
  ## NumberUtils
248
252
  Numbers related utilities
@@ -13,6 +13,54 @@ test('Tests for addBlankItem', () => {
13
13
  expect(options.length).toBe(3);
14
14
  });
15
15
 
16
+ test('Tests for addUrlParam', () => {
17
+ const url = 'https://www.etsoo.com';
18
+ const result = Utils.addUrlParam(url, 'a', 'b');
19
+ expect(result).toBe('https://www.etsoo.com/?a=b');
20
+ });
21
+
22
+ describe('Tests for addUrlParams', () => {
23
+ const url = 'https://www.etsoo.com';
24
+ const data = {
25
+ a: 'a',
26
+ b: false,
27
+ c: 123,
28
+ d: new Date(Date.UTC(2022, 0, 28, 10)),
29
+ e: [1, 2],
30
+ f: ['a', 'b'],
31
+ g: null
32
+ };
33
+ const result1 = Utils.addUrlParams(url, data);
34
+
35
+ test('addUrlParams', () => {
36
+ expect(result1).toBe(
37
+ 'https://www.etsoo.com/?a=a&b=false&c=123&d=2022-01-28T10%3A00%3A00.000Z&e=1&e=2&f=a&f=b&g='
38
+ );
39
+ });
40
+
41
+ const result2 = Utils.addUrlParams(url, data, true);
42
+
43
+ test('addUrlParams with array format', () => {
44
+ expect(result2).toBe(
45
+ 'https://www.etsoo.com/?a=a&b=false&c=123&d=2022-01-28T10%3A00%3A00.000Z&e=1%2C2&f=a%2Cb&g='
46
+ );
47
+ });
48
+
49
+ global.URL = undefined as any;
50
+
51
+ const result3 = url.addUrlParams(data);
52
+
53
+ test('addUrlParams with traditional way', () => {
54
+ expect(result3).toBe(result1);
55
+ });
56
+
57
+ const result4 = url.addUrlParams(data, true);
58
+
59
+ test('addUrlParams with traditional way and array format', () => {
60
+ expect(result4).toBe(result2);
61
+ });
62
+ });
63
+
16
64
  test('Tests for containChinese', () => {
17
65
  expect('123 abC'.containChinese()).toBeFalsy();
18
66
  expect('亿速思维'.containChinese()).toBeTruthy();
@@ -31,8 +31,15 @@ export declare namespace ExtendUtils {
31
31
  * Wait for condition meets and execute callback
32
32
  * requestAnimationFrame to replace setTimeout
33
33
  * @param callback Callback
34
- * @param checkReady Check ready, when it's a number, similar to setTimeout
34
+ * @param checkReady Check ready, when it's a number as miliseconds, similar to setTimeout
35
35
  * @returns cancel callback
36
36
  */
37
37
  function waitFor(callback: () => void, checkReady: ((spanTime: number) => boolean) | number): () => void;
38
+ /**
39
+ * Repeat interval for callback
40
+ * @param callback Callback
41
+ * @param miliseconds Miliseconds
42
+ * @returns cancel callback
43
+ */
44
+ function intervalFor(callback: () => void, miliseconds: number): () => void;
38
45
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExtendUtils = void 0;
4
+ const hasRequestAnimationFrame = typeof requestAnimationFrame === 'function';
4
5
  /**
5
6
  * Extend utilities
6
7
  */
@@ -85,38 +86,113 @@ var ExtendUtils;
85
86
  * Wait for condition meets and execute callback
86
87
  * requestAnimationFrame to replace setTimeout
87
88
  * @param callback Callback
88
- * @param checkReady Check ready, when it's a number, similar to setTimeout
89
+ * @param checkReady Check ready, when it's a number as miliseconds, similar to setTimeout
89
90
  * @returns cancel callback
90
91
  */
91
92
  function waitFor(callback, checkReady) {
92
- let startTime;
93
93
  let requestID;
94
- function doWait(time) {
95
- // Reset request id
96
- requestID = undefined;
97
- // First time
98
- if (startTime == null)
99
- startTime = time;
100
- // Ignore
101
- if (startTime === 0)
102
- return;
103
- const spanTime = startTime == null || time == null ? 0 : time - startTime;
104
- if (time != null &&
105
- (typeof checkReady === 'number'
106
- ? spanTime >= checkReady
107
- : checkReady(spanTime))) {
108
- callback();
94
+ if (hasRequestAnimationFrame) {
95
+ let lastTime;
96
+ function loop(timestamp) {
97
+ // Cancelled
98
+ if (requestID == null)
99
+ return;
100
+ if (lastTime === undefined) {
101
+ lastTime = timestamp;
102
+ }
103
+ const elapsed = timestamp - lastTime;
104
+ const isReady = typeof checkReady === 'number'
105
+ ? elapsed >= checkReady
106
+ : checkReady(elapsed);
107
+ if (isReady) {
108
+ callback();
109
+ }
110
+ else if (requestID != null) {
111
+ // May also be cancelled in callback or somewhere
112
+ requestID = requestAnimationFrame(loop);
113
+ }
109
114
  }
110
- else {
111
- requestID = requestAnimationFrame(doWait);
115
+ requestID = requestAnimationFrame(loop);
116
+ }
117
+ else if (typeof checkReady === 'number') {
118
+ requestID = setTimeout(callback, checkReady);
119
+ }
120
+ else {
121
+ // Bad practice to use setTimeout in this way, only for compatibility
122
+ const ms = 20;
123
+ let spanTime = 0;
124
+ let cr = checkReady;
125
+ function loop() {
126
+ // Cancelled
127
+ if (requestID == null)
128
+ return;
129
+ spanTime += ms;
130
+ if (cr(spanTime)) {
131
+ callback();
132
+ }
133
+ else if (requestID != null) {
134
+ // May also be cancelled in callback or somewhere
135
+ requestID = setTimeout(loop, ms);
136
+ }
112
137
  }
138
+ requestID = setTimeout(loop, ms);
113
139
  }
114
- doWait();
115
140
  return () => {
116
- if (requestID)
117
- cancelAnimationFrame(requestID);
118
- startTime = undefined;
141
+ if (requestID) {
142
+ if (hasRequestAnimationFrame && typeof requestID === 'number') {
143
+ cancelAnimationFrame(requestID);
144
+ }
145
+ else {
146
+ clearTimeout(requestID);
147
+ }
148
+ requestID = undefined;
149
+ }
119
150
  };
120
151
  }
121
152
  ExtendUtils.waitFor = waitFor;
153
+ /**
154
+ * Repeat interval for callback
155
+ * @param callback Callback
156
+ * @param miliseconds Miliseconds
157
+ * @returns cancel callback
158
+ */
159
+ function intervalFor(callback, miliseconds) {
160
+ let requestID;
161
+ if (hasRequestAnimationFrame) {
162
+ let lastTime;
163
+ function loop(timestamp) {
164
+ // Cancelled
165
+ if (requestID == null)
166
+ return;
167
+ if (lastTime === undefined) {
168
+ lastTime = timestamp;
169
+ }
170
+ const elapsed = timestamp - lastTime;
171
+ if (elapsed >= miliseconds) {
172
+ lastTime = timestamp;
173
+ callback();
174
+ }
175
+ if (requestID != null) {
176
+ // May also be cancelled in callback or somewhere
177
+ requestID = requestAnimationFrame(loop);
178
+ }
179
+ }
180
+ requestID = requestAnimationFrame(loop);
181
+ }
182
+ else {
183
+ requestID = setInterval(callback, miliseconds);
184
+ }
185
+ return () => {
186
+ if (requestID) {
187
+ if (hasRequestAnimationFrame && typeof requestID === 'number') {
188
+ cancelAnimationFrame(requestID);
189
+ }
190
+ else {
191
+ clearInterval(requestID);
192
+ }
193
+ requestID = undefined;
194
+ }
195
+ };
196
+ }
197
+ ExtendUtils.intervalFor = intervalFor;
122
198
  })(ExtendUtils || (exports.ExtendUtils = ExtendUtils = {}));
@@ -2,6 +2,23 @@ import { DataTypes, IdType } from './DataTypes';
2
2
  import { ParsedPath } from './types/ParsedPath';
3
3
  declare global {
4
4
  interface String {
5
+ /**
6
+ * Add parameter to URL
7
+ * @param this URL to add parameter
8
+ * @param name Parameter name
9
+ * @param value Parameter value
10
+ * @param arrayFormat Array format to array style or not to multiple fields
11
+ * @returns New URL
12
+ */
13
+ addUrlParam(this: string, name: string, value: DataTypes.Simple, arrayFormat?: boolean | string): string;
14
+ /**
15
+ * Add parameters to URL
16
+ * @param this URL to add parameters
17
+ * @param data Parameters
18
+ * @param arrayFormat Array format to array style or not to multiple fields
19
+ * @returns New URL
20
+ */
21
+ addUrlParams(this: string, data: DataTypes.SimpleObject, arrayFormat?: boolean | string): string;
5
22
  /**
6
23
  * Check the input string contains Chinese character or not
7
24
  * @param this Input
@@ -73,6 +90,23 @@ export declare namespace Utils {
73
90
  * @param blankLabel Blank label, default is ---
74
91
  */
75
92
  function addBlankItem<T extends object>(options: T[], idField?: string | keyof T, labelField?: unknown, blankLabel?: string): T[];
93
+ /**
94
+ * Add parameter to URL
95
+ * @param url URL to add parameter
96
+ * @param name Parameter name
97
+ * @param value Parameter value
98
+ * @param arrayFormat Array format to array style or not to multiple fields
99
+ * @returns New URL
100
+ */
101
+ function addUrlParam(url: string, name: string, value: DataTypes.Simple, arrayFormat?: boolean | string): string;
102
+ /**
103
+ * Add parameters to URL
104
+ * @param url URL to add parameters
105
+ * @param data Parameters
106
+ * @param arrayFormat Array format to array style or not to multiple fields
107
+ * @returns New URL
108
+ */
109
+ function addUrlParams(url: string, data: DataTypes.SimpleObject, arrayFormat?: boolean | string): string;
76
110
  /**
77
111
  * Base64 chars to number
78
112
  * @param base64Chars Base64 chars
package/lib/cjs/Utils.js CHANGED
@@ -7,6 +7,12 @@ exports.Utils = void 0;
7
7
  const DataTypes_1 = require("./DataTypes");
8
8
  const lodash_isequal_1 = __importDefault(require("lodash.isequal"));
9
9
  const DateUtils_1 = require("./DateUtils");
10
+ String.prototype.addUrlParam = function (name, value, arrayFormat) {
11
+ return Utils.addUrlParam(this, name, value, arrayFormat);
12
+ };
13
+ String.prototype.addUrlParams = function (data, arrayFormat) {
14
+ return Utils.addUrlParams(this, data, arrayFormat);
15
+ };
10
16
  String.prototype.containChinese = function () {
11
17
  const regExp = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]/g;
12
18
  return regExp.test(this);
@@ -89,6 +95,83 @@ var Utils;
89
95
  return options;
90
96
  }
91
97
  Utils.addBlankItem = addBlankItem;
98
+ /**
99
+ * Add parameter to URL
100
+ * @param url URL to add parameter
101
+ * @param name Parameter name
102
+ * @param value Parameter value
103
+ * @param arrayFormat Array format to array style or not to multiple fields
104
+ * @returns New URL
105
+ */
106
+ function addUrlParam(url, name, value, arrayFormat) {
107
+ return addUrlParams(url, { [name]: value }, arrayFormat);
108
+ }
109
+ Utils.addUrlParam = addUrlParam;
110
+ /**
111
+ * Add parameters to URL
112
+ * @param url URL to add parameters
113
+ * @param data Parameters
114
+ * @param arrayFormat Array format to array style or not to multiple fields
115
+ * @returns New URL
116
+ */
117
+ function addUrlParams(url, data, arrayFormat) {
118
+ if (typeof URL === 'undefined') {
119
+ if (url.includes('?')) {
120
+ url += '&';
121
+ }
122
+ else {
123
+ if (!url.endsWith('/'))
124
+ url = url + '/';
125
+ url += '?';
126
+ }
127
+ url += Object.entries(data)
128
+ .map(([key, value]) => {
129
+ let v;
130
+ if (Array.isArray(value)) {
131
+ if (arrayFormat == null || arrayFormat === false) {
132
+ return value
133
+ .map((item) => `${key}=${encodeURIComponent(`${item}`)}`)
134
+ .join('&');
135
+ }
136
+ else {
137
+ v = value.join(arrayFormat ? ',' : arrayFormat);
138
+ }
139
+ }
140
+ else if (value instanceof Date) {
141
+ v = value.toJSON();
142
+ }
143
+ else {
144
+ v = value == null ? '' : `${value}`;
145
+ }
146
+ return `${key}=${encodeURIComponent(v)}`;
147
+ })
148
+ .join('&');
149
+ return url;
150
+ }
151
+ else {
152
+ const urlObj = new URL(url);
153
+ Object.entries(data).forEach(([key, value]) => {
154
+ if (Array.isArray(value)) {
155
+ if (arrayFormat == null || arrayFormat === false) {
156
+ value.forEach((item) => {
157
+ urlObj.searchParams.append(key, `${item}`);
158
+ });
159
+ }
160
+ else {
161
+ urlObj.searchParams.append(key, value.join(arrayFormat ? ',' : arrayFormat));
162
+ }
163
+ }
164
+ else if (value instanceof Date) {
165
+ urlObj.searchParams.append(key, value.toJSON());
166
+ }
167
+ else {
168
+ urlObj.searchParams.append(key, `${value == null ? '' : value}`);
169
+ }
170
+ });
171
+ return urlObj.toString();
172
+ }
173
+ }
174
+ Utils.addUrlParams = addUrlParams;
92
175
  /**
93
176
  * Base64 chars to number
94
177
  * @param base64Chars Base64 chars
@@ -31,8 +31,15 @@ export declare namespace ExtendUtils {
31
31
  * Wait for condition meets and execute callback
32
32
  * requestAnimationFrame to replace setTimeout
33
33
  * @param callback Callback
34
- * @param checkReady Check ready, when it's a number, similar to setTimeout
34
+ * @param checkReady Check ready, when it's a number as miliseconds, similar to setTimeout
35
35
  * @returns cancel callback
36
36
  */
37
37
  function waitFor(callback: () => void, checkReady: ((spanTime: number) => boolean) | number): () => void;
38
+ /**
39
+ * Repeat interval for callback
40
+ * @param callback Callback
41
+ * @param miliseconds Miliseconds
42
+ * @returns cancel callback
43
+ */
44
+ function intervalFor(callback: () => void, miliseconds: number): () => void;
38
45
  }
@@ -1,3 +1,4 @@
1
+ const hasRequestAnimationFrame = typeof requestAnimationFrame === 'function';
1
2
  /**
2
3
  * Extend utilities
3
4
  */
@@ -82,38 +83,113 @@ export var ExtendUtils;
82
83
  * Wait for condition meets and execute callback
83
84
  * requestAnimationFrame to replace setTimeout
84
85
  * @param callback Callback
85
- * @param checkReady Check ready, when it's a number, similar to setTimeout
86
+ * @param checkReady Check ready, when it's a number as miliseconds, similar to setTimeout
86
87
  * @returns cancel callback
87
88
  */
88
89
  function waitFor(callback, checkReady) {
89
- let startTime;
90
90
  let requestID;
91
- function doWait(time) {
92
- // Reset request id
93
- requestID = undefined;
94
- // First time
95
- if (startTime == null)
96
- startTime = time;
97
- // Ignore
98
- if (startTime === 0)
99
- return;
100
- const spanTime = startTime == null || time == null ? 0 : time - startTime;
101
- if (time != null &&
102
- (typeof checkReady === 'number'
103
- ? spanTime >= checkReady
104
- : checkReady(spanTime))) {
105
- callback();
91
+ if (hasRequestAnimationFrame) {
92
+ let lastTime;
93
+ function loop(timestamp) {
94
+ // Cancelled
95
+ if (requestID == null)
96
+ return;
97
+ if (lastTime === undefined) {
98
+ lastTime = timestamp;
99
+ }
100
+ const elapsed = timestamp - lastTime;
101
+ const isReady = typeof checkReady === 'number'
102
+ ? elapsed >= checkReady
103
+ : checkReady(elapsed);
104
+ if (isReady) {
105
+ callback();
106
+ }
107
+ else if (requestID != null) {
108
+ // May also be cancelled in callback or somewhere
109
+ requestID = requestAnimationFrame(loop);
110
+ }
106
111
  }
107
- else {
108
- requestID = requestAnimationFrame(doWait);
112
+ requestID = requestAnimationFrame(loop);
113
+ }
114
+ else if (typeof checkReady === 'number') {
115
+ requestID = setTimeout(callback, checkReady);
116
+ }
117
+ else {
118
+ // Bad practice to use setTimeout in this way, only for compatibility
119
+ const ms = 20;
120
+ let spanTime = 0;
121
+ let cr = checkReady;
122
+ function loop() {
123
+ // Cancelled
124
+ if (requestID == null)
125
+ return;
126
+ spanTime += ms;
127
+ if (cr(spanTime)) {
128
+ callback();
129
+ }
130
+ else if (requestID != null) {
131
+ // May also be cancelled in callback or somewhere
132
+ requestID = setTimeout(loop, ms);
133
+ }
109
134
  }
135
+ requestID = setTimeout(loop, ms);
110
136
  }
111
- doWait();
112
137
  return () => {
113
- if (requestID)
114
- cancelAnimationFrame(requestID);
115
- startTime = undefined;
138
+ if (requestID) {
139
+ if (hasRequestAnimationFrame && typeof requestID === 'number') {
140
+ cancelAnimationFrame(requestID);
141
+ }
142
+ else {
143
+ clearTimeout(requestID);
144
+ }
145
+ requestID = undefined;
146
+ }
116
147
  };
117
148
  }
118
149
  ExtendUtils.waitFor = waitFor;
150
+ /**
151
+ * Repeat interval for callback
152
+ * @param callback Callback
153
+ * @param miliseconds Miliseconds
154
+ * @returns cancel callback
155
+ */
156
+ function intervalFor(callback, miliseconds) {
157
+ let requestID;
158
+ if (hasRequestAnimationFrame) {
159
+ let lastTime;
160
+ function loop(timestamp) {
161
+ // Cancelled
162
+ if (requestID == null)
163
+ return;
164
+ if (lastTime === undefined) {
165
+ lastTime = timestamp;
166
+ }
167
+ const elapsed = timestamp - lastTime;
168
+ if (elapsed >= miliseconds) {
169
+ lastTime = timestamp;
170
+ callback();
171
+ }
172
+ if (requestID != null) {
173
+ // May also be cancelled in callback or somewhere
174
+ requestID = requestAnimationFrame(loop);
175
+ }
176
+ }
177
+ requestID = requestAnimationFrame(loop);
178
+ }
179
+ else {
180
+ requestID = setInterval(callback, miliseconds);
181
+ }
182
+ return () => {
183
+ if (requestID) {
184
+ if (hasRequestAnimationFrame && typeof requestID === 'number') {
185
+ cancelAnimationFrame(requestID);
186
+ }
187
+ else {
188
+ clearInterval(requestID);
189
+ }
190
+ requestID = undefined;
191
+ }
192
+ };
193
+ }
194
+ ExtendUtils.intervalFor = intervalFor;
119
195
  })(ExtendUtils || (ExtendUtils = {}));
@@ -2,6 +2,23 @@ import { DataTypes, IdType } from './DataTypes';
2
2
  import { ParsedPath } from './types/ParsedPath';
3
3
  declare global {
4
4
  interface String {
5
+ /**
6
+ * Add parameter to URL
7
+ * @param this URL to add parameter
8
+ * @param name Parameter name
9
+ * @param value Parameter value
10
+ * @param arrayFormat Array format to array style or not to multiple fields
11
+ * @returns New URL
12
+ */
13
+ addUrlParam(this: string, name: string, value: DataTypes.Simple, arrayFormat?: boolean | string): string;
14
+ /**
15
+ * Add parameters to URL
16
+ * @param this URL to add parameters
17
+ * @param data Parameters
18
+ * @param arrayFormat Array format to array style or not to multiple fields
19
+ * @returns New URL
20
+ */
21
+ addUrlParams(this: string, data: DataTypes.SimpleObject, arrayFormat?: boolean | string): string;
5
22
  /**
6
23
  * Check the input string contains Chinese character or not
7
24
  * @param this Input
@@ -73,6 +90,23 @@ export declare namespace Utils {
73
90
  * @param blankLabel Blank label, default is ---
74
91
  */
75
92
  function addBlankItem<T extends object>(options: T[], idField?: string | keyof T, labelField?: unknown, blankLabel?: string): T[];
93
+ /**
94
+ * Add parameter to URL
95
+ * @param url URL to add parameter
96
+ * @param name Parameter name
97
+ * @param value Parameter value
98
+ * @param arrayFormat Array format to array style or not to multiple fields
99
+ * @returns New URL
100
+ */
101
+ function addUrlParam(url: string, name: string, value: DataTypes.Simple, arrayFormat?: boolean | string): string;
102
+ /**
103
+ * Add parameters to URL
104
+ * @param url URL to add parameters
105
+ * @param data Parameters
106
+ * @param arrayFormat Array format to array style or not to multiple fields
107
+ * @returns New URL
108
+ */
109
+ function addUrlParams(url: string, data: DataTypes.SimpleObject, arrayFormat?: boolean | string): string;
76
110
  /**
77
111
  * Base64 chars to number
78
112
  * @param base64Chars Base64 chars
package/lib/mjs/Utils.js CHANGED
@@ -1,6 +1,12 @@
1
1
  import { DataTypes } from './DataTypes';
2
2
  import isEqual from 'lodash.isequal';
3
3
  import { DateUtils } from './DateUtils';
4
+ String.prototype.addUrlParam = function (name, value, arrayFormat) {
5
+ return Utils.addUrlParam(this, name, value, arrayFormat);
6
+ };
7
+ String.prototype.addUrlParams = function (data, arrayFormat) {
8
+ return Utils.addUrlParams(this, data, arrayFormat);
9
+ };
4
10
  String.prototype.containChinese = function () {
5
11
  const regExp = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]/g;
6
12
  return regExp.test(this);
@@ -83,6 +89,83 @@ export var Utils;
83
89
  return options;
84
90
  }
85
91
  Utils.addBlankItem = addBlankItem;
92
+ /**
93
+ * Add parameter to URL
94
+ * @param url URL to add parameter
95
+ * @param name Parameter name
96
+ * @param value Parameter value
97
+ * @param arrayFormat Array format to array style or not to multiple fields
98
+ * @returns New URL
99
+ */
100
+ function addUrlParam(url, name, value, arrayFormat) {
101
+ return addUrlParams(url, { [name]: value }, arrayFormat);
102
+ }
103
+ Utils.addUrlParam = addUrlParam;
104
+ /**
105
+ * Add parameters to URL
106
+ * @param url URL to add parameters
107
+ * @param data Parameters
108
+ * @param arrayFormat Array format to array style or not to multiple fields
109
+ * @returns New URL
110
+ */
111
+ function addUrlParams(url, data, arrayFormat) {
112
+ if (typeof URL === 'undefined') {
113
+ if (url.includes('?')) {
114
+ url += '&';
115
+ }
116
+ else {
117
+ if (!url.endsWith('/'))
118
+ url = url + '/';
119
+ url += '?';
120
+ }
121
+ url += Object.entries(data)
122
+ .map(([key, value]) => {
123
+ let v;
124
+ if (Array.isArray(value)) {
125
+ if (arrayFormat == null || arrayFormat === false) {
126
+ return value
127
+ .map((item) => `${key}=${encodeURIComponent(`${item}`)}`)
128
+ .join('&');
129
+ }
130
+ else {
131
+ v = value.join(arrayFormat ? ',' : arrayFormat);
132
+ }
133
+ }
134
+ else if (value instanceof Date) {
135
+ v = value.toJSON();
136
+ }
137
+ else {
138
+ v = value == null ? '' : `${value}`;
139
+ }
140
+ return `${key}=${encodeURIComponent(v)}`;
141
+ })
142
+ .join('&');
143
+ return url;
144
+ }
145
+ else {
146
+ const urlObj = new URL(url);
147
+ Object.entries(data).forEach(([key, value]) => {
148
+ if (Array.isArray(value)) {
149
+ if (arrayFormat == null || arrayFormat === false) {
150
+ value.forEach((item) => {
151
+ urlObj.searchParams.append(key, `${item}`);
152
+ });
153
+ }
154
+ else {
155
+ urlObj.searchParams.append(key, value.join(arrayFormat ? ',' : arrayFormat));
156
+ }
157
+ }
158
+ else if (value instanceof Date) {
159
+ urlObj.searchParams.append(key, value.toJSON());
160
+ }
161
+ else {
162
+ urlObj.searchParams.append(key, `${value == null ? '' : value}`);
163
+ }
164
+ });
165
+ return urlObj.toString();
166
+ }
167
+ }
168
+ Utils.addUrlParams = addUrlParams;
86
169
  /**
87
170
  * Base64 chars to number
88
171
  * @param base64Chars Base64 chars
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/shared",
3
- "version": "1.2.45",
3
+ "version": "1.2.47",
4
4
  "description": "TypeScript shared utilities and functions",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -59,7 +59,7 @@
59
59
  "jest": "^29.7.0",
60
60
  "jest-environment-jsdom": "^29.7.0",
61
61
  "ts-jest": "^29.2.5",
62
- "typescript": "^5.6.2"
62
+ "typescript": "^5.6.3"
63
63
  },
64
64
  "dependencies": {
65
65
  "lodash.isequal": "^4.5.0"
@@ -1,5 +1,7 @@
1
1
  import { DelayedExecutorType } from './types/DelayedExecutorType';
2
2
 
3
+ const hasRequestAnimationFrame = typeof requestAnimationFrame === 'function';
4
+
3
5
  /**
4
6
  * Extend utilities
5
7
  */
@@ -89,44 +91,119 @@ export namespace ExtendUtils {
89
91
  * Wait for condition meets and execute callback
90
92
  * requestAnimationFrame to replace setTimeout
91
93
  * @param callback Callback
92
- * @param checkReady Check ready, when it's a number, similar to setTimeout
94
+ * @param checkReady Check ready, when it's a number as miliseconds, similar to setTimeout
93
95
  * @returns cancel callback
94
96
  */
95
97
  export function waitFor(
96
98
  callback: () => void,
97
99
  checkReady: ((spanTime: number) => boolean) | number
98
100
  ) {
99
- let startTime: number | undefined;
100
- let requestID: number | undefined;
101
- function doWait(time?: number) {
102
- // Reset request id
103
- requestID = undefined;
104
-
105
- // First time
106
- if (startTime == null) startTime = time;
107
-
108
- // Ignore
109
- if (startTime === 0) return;
110
-
111
- const spanTime =
112
- startTime == null || time == null ? 0 : time - startTime;
113
- if (
114
- time != null &&
115
- (typeof checkReady === 'number'
116
- ? spanTime >= checkReady
117
- : checkReady(spanTime))
118
- ) {
119
- callback();
120
- } else {
121
- requestID = requestAnimationFrame(doWait);
101
+ let requestID: number | undefined | NodeJS.Timeout;
102
+
103
+ if (hasRequestAnimationFrame) {
104
+ let lastTime: number | undefined;
105
+ function loop(timestamp: number) {
106
+ // Cancelled
107
+ if (requestID == null) return;
108
+
109
+ if (lastTime === undefined) {
110
+ lastTime = timestamp;
111
+ }
112
+
113
+ const elapsed = timestamp - lastTime;
114
+
115
+ const isReady =
116
+ typeof checkReady === 'number'
117
+ ? elapsed >= checkReady
118
+ : checkReady(elapsed);
119
+
120
+ if (isReady) {
121
+ callback();
122
+ } else if (requestID != null) {
123
+ // May also be cancelled in callback or somewhere
124
+ requestID = requestAnimationFrame(loop);
125
+ }
122
126
  }
127
+ requestID = requestAnimationFrame(loop);
128
+ } else if (typeof checkReady === 'number') {
129
+ requestID = setTimeout(callback, checkReady);
130
+ } else {
131
+ // Bad practice to use setTimeout in this way, only for compatibility
132
+ const ms = 20;
133
+ let spanTime = 0;
134
+ let cr = checkReady;
135
+ function loop() {
136
+ // Cancelled
137
+ if (requestID == null) return;
138
+
139
+ spanTime += ms;
140
+
141
+ if (cr(spanTime)) {
142
+ callback();
143
+ } else if (requestID != null) {
144
+ // May also be cancelled in callback or somewhere
145
+ requestID = setTimeout(loop, ms);
146
+ }
147
+ }
148
+ requestID = setTimeout(loop, ms);
123
149
  }
124
150
 
125
- doWait();
151
+ return () => {
152
+ if (requestID) {
153
+ if (hasRequestAnimationFrame && typeof requestID === 'number') {
154
+ cancelAnimationFrame(requestID);
155
+ } else {
156
+ clearTimeout(requestID);
157
+ }
158
+ requestID = undefined;
159
+ }
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Repeat interval for callback
165
+ * @param callback Callback
166
+ * @param miliseconds Miliseconds
167
+ * @returns cancel callback
168
+ */
169
+ export function intervalFor(callback: () => void, miliseconds: number) {
170
+ let requestID: number | undefined | NodeJS.Timer;
171
+
172
+ if (hasRequestAnimationFrame) {
173
+ let lastTime: number | undefined;
174
+ function loop(timestamp: number) {
175
+ // Cancelled
176
+ if (requestID == null) return;
177
+
178
+ if (lastTime === undefined) {
179
+ lastTime = timestamp;
180
+ }
181
+
182
+ const elapsed = timestamp - lastTime;
183
+ if (elapsed >= miliseconds) {
184
+ lastTime = timestamp;
185
+ callback();
186
+ }
187
+
188
+ if (requestID != null) {
189
+ // May also be cancelled in callback or somewhere
190
+ requestID = requestAnimationFrame(loop);
191
+ }
192
+ }
193
+ requestID = requestAnimationFrame(loop);
194
+ } else {
195
+ requestID = setInterval(callback, miliseconds);
196
+ }
126
197
 
127
198
  return () => {
128
- if (requestID) cancelAnimationFrame(requestID);
129
- startTime = undefined;
199
+ if (requestID) {
200
+ if (hasRequestAnimationFrame && typeof requestID === 'number') {
201
+ cancelAnimationFrame(requestID);
202
+ } else {
203
+ clearInterval(requestID);
204
+ }
205
+ requestID = undefined;
206
+ }
130
207
  };
131
208
  }
132
209
  }
package/src/Utils.ts CHANGED
@@ -5,6 +5,34 @@ import { ParsedPath } from './types/ParsedPath';
5
5
 
6
6
  declare global {
7
7
  interface String {
8
+ /**
9
+ * Add parameter to URL
10
+ * @param this URL to add parameter
11
+ * @param name Parameter name
12
+ * @param value Parameter value
13
+ * @param arrayFormat Array format to array style or not to multiple fields
14
+ * @returns New URL
15
+ */
16
+ addUrlParam(
17
+ this: string,
18
+ name: string,
19
+ value: DataTypes.Simple,
20
+ arrayFormat?: boolean | string
21
+ ): string;
22
+
23
+ /**
24
+ * Add parameters to URL
25
+ * @param this URL to add parameters
26
+ * @param data Parameters
27
+ * @param arrayFormat Array format to array style or not to multiple fields
28
+ * @returns New URL
29
+ */
30
+ addUrlParams(
31
+ this: string,
32
+ data: DataTypes.SimpleObject,
33
+ arrayFormat?: boolean | string
34
+ ): string;
35
+
8
36
  /**
9
37
  * Check the input string contains Chinese character or not
10
38
  * @param this Input
@@ -74,6 +102,23 @@ declare global {
74
102
  }
75
103
  }
76
104
 
105
+ String.prototype.addUrlParam = function (
106
+ this: string,
107
+ name: string,
108
+ value: DataTypes.Simple,
109
+ arrayFormat?: boolean | string
110
+ ) {
111
+ return Utils.addUrlParam(this, name, value, arrayFormat);
112
+ };
113
+
114
+ String.prototype.addUrlParams = function (
115
+ this: string,
116
+ data: DataTypes.SimpleObject,
117
+ arrayFormat?: boolean | string
118
+ ) {
119
+ return Utils.addUrlParams(this, data, arrayFormat);
120
+ };
121
+
77
122
  String.prototype.containChinese = function (this: string): boolean {
78
123
  const regExp =
79
124
  /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]/g;
@@ -180,6 +225,97 @@ export namespace Utils {
180
225
  return options;
181
226
  }
182
227
 
228
+ /**
229
+ * Add parameter to URL
230
+ * @param url URL to add parameter
231
+ * @param name Parameter name
232
+ * @param value Parameter value
233
+ * @param arrayFormat Array format to array style or not to multiple fields
234
+ * @returns New URL
235
+ */
236
+ export function addUrlParam(
237
+ url: string,
238
+ name: string,
239
+ value: DataTypes.Simple,
240
+ arrayFormat?: boolean | string
241
+ ) {
242
+ return addUrlParams(url, { [name]: value }, arrayFormat);
243
+ }
244
+
245
+ /**
246
+ * Add parameters to URL
247
+ * @param url URL to add parameters
248
+ * @param data Parameters
249
+ * @param arrayFormat Array format to array style or not to multiple fields
250
+ * @returns New URL
251
+ */
252
+ export function addUrlParams(
253
+ url: string,
254
+ data: DataTypes.SimpleObject,
255
+ arrayFormat?: boolean | string
256
+ ) {
257
+ if (typeof URL === 'undefined') {
258
+ if (url.includes('?')) {
259
+ url += '&';
260
+ } else {
261
+ if (!url.endsWith('/')) url = url + '/';
262
+ url += '?';
263
+ }
264
+
265
+ url += Object.entries(data)
266
+ .map(([key, value]) => {
267
+ let v: string;
268
+ if (Array.isArray(value)) {
269
+ if (arrayFormat == null || arrayFormat === false) {
270
+ return value
271
+ .map(
272
+ (item) =>
273
+ `${key}=${encodeURIComponent(
274
+ `${item}`
275
+ )}`
276
+ )
277
+ .join('&');
278
+ } else {
279
+ v = value.join(arrayFormat ? ',' : arrayFormat);
280
+ }
281
+ } else if (value instanceof Date) {
282
+ v = value.toJSON();
283
+ } else {
284
+ v = value == null ? '' : `${value}`;
285
+ }
286
+
287
+ return `${key}=${encodeURIComponent(v)}`;
288
+ })
289
+ .join('&');
290
+
291
+ return url;
292
+ } else {
293
+ const urlObj = new URL(url);
294
+ Object.entries(data).forEach(([key, value]) => {
295
+ if (Array.isArray(value)) {
296
+ if (arrayFormat == null || arrayFormat === false) {
297
+ value.forEach((item) => {
298
+ urlObj.searchParams.append(key, `${item}`);
299
+ });
300
+ } else {
301
+ urlObj.searchParams.append(
302
+ key,
303
+ value.join(arrayFormat ? ',' : arrayFormat)
304
+ );
305
+ }
306
+ } else if (value instanceof Date) {
307
+ urlObj.searchParams.append(key, value.toJSON());
308
+ } else {
309
+ urlObj.searchParams.append(
310
+ key,
311
+ `${value == null ? '' : value}`
312
+ );
313
+ }
314
+ });
315
+ return urlObj.toString();
316
+ }
317
+ }
318
+
183
319
  /**
184
320
  * Base64 chars to number
185
321
  * @param base64Chars Base64 chars