@etsoo/shared 1.2.13 → 1.2.15

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
@@ -278,6 +278,7 @@ String and other related utilities
278
278
  |formatInitial|Format inital character to lower case or upper case|
279
279
  |formatString|Format string with parameters|
280
280
  |getDataChanges|Get data changed fields with input data updated|
281
+ |getNestedValue|Get nested value from object|
281
282
  |getTimeZone|Get time zone|
282
283
  |hideData|Hide data|
283
284
  |hideEmail|Hide email data|
@@ -291,11 +292,13 @@ String and other related utilities
291
292
  |objectEqual|Test two objects are equal or not|
292
293
  |objectKeys|Get two object's unqiue properties|
293
294
  |objectUpdated|Get the new object's updated fields contrast to the previous object|
295
+ |parseJsonArray|Try to parse JSON input to array|
294
296
  |parsePath|Parse path similar with node.js path.parse|
295
297
  |parseString|Parse string (JSON) to specific type|
296
298
  |removeNonLetters|Remove non letters (0-9, a-z, A-Z)|
297
299
  |replaceNullOrEmpty|Replace null or empty with default value|
298
300
  |setLabels|Set source with new labels|
301
+ |setNestedValue|Set nested value to object|
299
302
  |snakeNameToWord|Snake name to works, 'snake_name' to 'Snake Name'|
300
303
  |sortByFavor|Sort array by favored values|
301
304
  |sortByFieldFavor|Sort array by favored field values|
@@ -1,5 +1,6 @@
1
1
  import { DomUtils } from '../src/DomUtils';
2
2
  import { DataTypes } from '../src/DataTypes';
3
+ import { DateUtils } from '../src/DateUtils';
3
4
 
4
5
  // Implement for tests
5
6
  class Rect implements DOMRect {
@@ -308,3 +309,20 @@ test('Tests for setFocus', () => {
308
309
  DomUtils.setFocus({ test: 'No content' }, container);
309
310
  expect(focus).toBeCalledTimes(2);
310
311
  });
312
+
313
+ test('Tests for getInputValue', () => {
314
+ // Arrange
315
+ const input = document.createElement('input');
316
+ input.type = 'datetime-local';
317
+ input.value = DateUtils.formatForInput('2023-09-21T23:08', false) ?? '';
318
+
319
+ // Act
320
+ const result = DomUtils.getInputValue(input);
321
+
322
+ // Assert
323
+ expect(result).not.toBeUndefined();
324
+ expect(result instanceof Date).toBeTruthy();
325
+ if (result instanceof Date) {
326
+ expect(result.getDate()).toBe(21);
327
+ }
328
+ });
@@ -188,6 +188,12 @@ test('Tests for objectEqual', () => {
188
188
  expect(Utils.objectEqual(obj1, obj2, ['a'], 2)).toBeFalsy();
189
189
  });
190
190
 
191
+ test('Tests for parseJsonArray', () => {
192
+ expect(Utils.parseJsonArray('[1, 3, "a"]', 0)).toBeUndefined();
193
+ expect(Utils.parseJsonArray('[1, 3, "a"]')).not.toBeUndefined();
194
+ expect(Utils.parseJsonArray('1, 3, 9')).not.toBeUndefined();
195
+ });
196
+
191
197
  test('Tests for objectUpdated', () => {
192
198
  const objPrev = { a: 1, b: 'abc', c: true, d: null, f: [1, 2] };
193
199
  const objNew = { a: 2, b: 'abc', d: new Date(), f: [1, 2, 3], g: true };
@@ -230,6 +236,15 @@ test('Tests for mergeClasses', () => {
230
236
  expect(Utils.mergeClasses('a', '', 'b ', undefined, 'c')).toBe('a b c');
231
237
  });
232
238
 
239
+ test('Tests for getNestedValue', () => {
240
+ const obj = { jsonData: { photoSize: [200, 100], supportResizing: true } };
241
+ expect(Utils.getNestedValue(obj, 'jsonData.supportResizing')).toBeTruthy();
242
+ expect(
243
+ Array.isArray(Utils.getNestedValue(obj, 'jsonData.photoSize'))
244
+ ).toBeTruthy();
245
+ expect(Utils.getNestedValue(obj, 'jsonData.unknown')).toBeUndefined();
246
+ });
247
+
233
248
  test('Tests for getResult', () => {
234
249
  // Arrange
235
250
  type test = ((visible: boolean) => number) | number;
@@ -281,6 +296,16 @@ test('Tests for parsePath, Windows path', () => {
281
296
  expect(result.name).toBe('file');
282
297
  });
283
298
 
299
+ test('Tests for setNestedValue', () => {
300
+ const obj = { jsonData: { photoSize: [200, 100], supportResizing: true } };
301
+
302
+ Utils.setNestedValue(obj, 'jsonData.supportResizing', false);
303
+ expect(obj.jsonData.supportResizing).toBeFalsy();
304
+
305
+ Utils.setNestedValue(obj, 'jsonData.newProperty.value', 125);
306
+ expect(Reflect.get((obj.jsonData as any).newProperty, 'value')).toBe(125);
307
+ });
308
+
284
309
  test('Tests for sortByFavor', () => {
285
310
  const items = [1, 2, 3, 4, 5, 6, 7];
286
311
  expect(Utils.sortByFavor(items, [5, 1, 3])).toStrictEqual([
@@ -108,7 +108,7 @@ export declare namespace DomUtils {
108
108
  * @param input HTML input
109
109
  * @returns Result
110
110
  */
111
- function getInputValue(input: HTMLInputElement): string | number | Date | null;
111
+ function getInputValue(input: HTMLInputElement): string | number | Date | null | undefined;
112
112
  /**
113
113
  * Get an unique key combined with current URL
114
114
  * @param key Key
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DomUtils = void 0;
4
4
  /// <reference lib="dom" />
5
5
  const DataTypes_1 = require("./DataTypes");
6
+ const DateUtils_1 = require("./DateUtils");
6
7
  if (typeof navigator === 'undefined') {
7
8
  // Test mock only
8
9
  global.navigator = { language: 'en-US' };
@@ -381,6 +382,7 @@ var DomUtils;
381
382
  * @returns Result
382
383
  */
383
384
  function getInputValue(input) {
385
+ var _a;
384
386
  const type = input.type;
385
387
  if (type === 'number' || type === 'range') {
386
388
  const num = input.valueAsNumber;
@@ -389,7 +391,7 @@ var DomUtils;
389
391
  return num;
390
392
  }
391
393
  else if (type === 'date' || type === 'datetime-local')
392
- return input.valueAsDate;
394
+ return (_a = input.valueAsDate) !== null && _a !== void 0 ? _a : DateUtils_1.DateUtils.parse(input.value);
393
395
  return input.value;
394
396
  }
395
397
  DomUtils.getInputValue = getInputValue;
@@ -135,6 +135,13 @@ export declare namespace Utils {
135
135
  * @returns
136
136
  */
137
137
  function getDataChanges(input: object, initData: object, ignoreFields?: string[]): string[];
138
+ /**
139
+ * Get nested value from object
140
+ * @param data Data
141
+ * @param name Field name, support property chain like 'jsonData.logSize'
142
+ * @returns Result
143
+ */
144
+ function getNestedValue(data: object, name: string): any;
138
145
  /**
139
146
  * Get input function or value result
140
147
  * @param input Input function or value
@@ -207,6 +214,13 @@ export declare namespace Utils {
207
214
  * @returns Updated fields
208
215
  */
209
216
  function objectUpdated(objNew: object, objPrev: object, ignoreFields?: string[], strict?: number): string[];
217
+ /**
218
+ * Try to parse JSON input to array
219
+ * @param input JSON input
220
+ * @param checkValue Type check value
221
+ * @returns Result
222
+ */
223
+ function parseJsonArray<T>(input: string, checkValue?: T): T[] | undefined;
210
224
  /**
211
225
  * Parse string (JSON) to specific type, no type conversion
212
226
  * For type conversion, please use DataTypes.convert
@@ -248,6 +262,13 @@ export declare namespace Utils {
248
262
  * @param firstOnly Only convert the first word to upper case
249
263
  */
250
264
  const snakeNameToWord: (name: string, firstOnly?: boolean) => string;
265
+ /**
266
+ * Set nested value to object
267
+ * @param data Data
268
+ * @param name Field name, support property chain like 'jsonData.logSize'
269
+ * @param value Value
270
+ */
271
+ function setNestedValue(data: object, name: string, value: unknown): void;
251
272
  /**
252
273
  * Parse path similar with node.js path.parse
253
274
  * @param path Input path
package/lib/cjs/Utils.js CHANGED
@@ -239,6 +239,36 @@ var Utils;
239
239
  return changes;
240
240
  }
241
241
  Utils.getDataChanges = getDataChanges;
242
+ /**
243
+ * Get nested value from object
244
+ * @param data Data
245
+ * @param name Field name, support property chain like 'jsonData.logSize'
246
+ * @returns Result
247
+ */
248
+ function getNestedValue(data, name) {
249
+ const properties = name.split('.');
250
+ const len = properties.length;
251
+ if (len === 1) {
252
+ return Reflect.get(data, name);
253
+ }
254
+ else {
255
+ let curr = data;
256
+ for (let i = 0; i < len; i++) {
257
+ const property = properties[i];
258
+ if (i + 1 === len) {
259
+ return Reflect.get(curr, property);
260
+ }
261
+ else {
262
+ let p = Reflect.get(curr, property);
263
+ if (p == null) {
264
+ return undefined;
265
+ }
266
+ curr = p;
267
+ }
268
+ }
269
+ }
270
+ }
271
+ Utils.getNestedValue = getNestedValue;
242
272
  /**
243
273
  * Get input function or value result
244
274
  * @param input Input function or value
@@ -389,6 +419,30 @@ var Utils;
389
419
  return fields;
390
420
  }
391
421
  Utils.objectUpdated = objectUpdated;
422
+ /**
423
+ * Try to parse JSON input to array
424
+ * @param input JSON input
425
+ * @param checkValue Type check value
426
+ * @returns Result
427
+ */
428
+ function parseJsonArray(input, checkValue) {
429
+ try {
430
+ if (!input.startsWith('['))
431
+ input = `[${input}]`;
432
+ const array = JSON.parse(input);
433
+ const type = typeof checkValue;
434
+ if (Array.isArray(array) &&
435
+ (checkValue == null ||
436
+ !array.some((item) => typeof item !== type))) {
437
+ return array;
438
+ }
439
+ }
440
+ catch (e) {
441
+ console.log('parseJsonArray', input, e);
442
+ }
443
+ return;
444
+ }
445
+ Utils.parseJsonArray = parseJsonArray;
392
446
  /**
393
447
  * Parse string (JSON) to specific type, no type conversion
394
448
  * When return type depends on parameter value, uses function overloading, otherwise uses conditional type
@@ -484,6 +538,36 @@ var Utils;
484
538
  return -1;
485
539
  return n1 - n2;
486
540
  }
541
+ /**
542
+ * Set nested value to object
543
+ * @param data Data
544
+ * @param name Field name, support property chain like 'jsonData.logSize'
545
+ * @param value Value
546
+ */
547
+ function setNestedValue(data, name, value) {
548
+ const properties = name.split('.');
549
+ const len = properties.length;
550
+ if (len === 1)
551
+ Reflect.set(data, name, value);
552
+ else {
553
+ let curr = data;
554
+ for (let i = 0; i < len; i++) {
555
+ const property = properties[i];
556
+ if (i + 1 === len) {
557
+ Reflect.set(curr, property, value);
558
+ }
559
+ else {
560
+ let p = Reflect.get(curr, property);
561
+ if (p == null) {
562
+ p = {};
563
+ Reflect.set(curr, property, p);
564
+ }
565
+ curr = p;
566
+ }
567
+ }
568
+ }
569
+ }
570
+ Utils.setNestedValue = setNestedValue;
487
571
  /**
488
572
  * Parse path similar with node.js path.parse
489
573
  * @param path Input path
@@ -108,7 +108,7 @@ export declare namespace DomUtils {
108
108
  * @param input HTML input
109
109
  * @returns Result
110
110
  */
111
- function getInputValue(input: HTMLInputElement): string | number | Date | null;
111
+ function getInputValue(input: HTMLInputElement): string | number | Date | null | undefined;
112
112
  /**
113
113
  * Get an unique key combined with current URL
114
114
  * @param key Key
@@ -1,5 +1,6 @@
1
1
  /// <reference lib="dom" />
2
2
  import { DataTypes } from './DataTypes';
3
+ import { DateUtils } from './DateUtils';
3
4
  if (typeof navigator === 'undefined') {
4
5
  // Test mock only
5
6
  global.navigator = { language: 'en-US' };
@@ -378,6 +379,7 @@ export var DomUtils;
378
379
  * @returns Result
379
380
  */
380
381
  function getInputValue(input) {
382
+ var _a;
381
383
  const type = input.type;
382
384
  if (type === 'number' || type === 'range') {
383
385
  const num = input.valueAsNumber;
@@ -386,7 +388,7 @@ export var DomUtils;
386
388
  return num;
387
389
  }
388
390
  else if (type === 'date' || type === 'datetime-local')
389
- return input.valueAsDate;
391
+ return (_a = input.valueAsDate) !== null && _a !== void 0 ? _a : DateUtils.parse(input.value);
390
392
  return input.value;
391
393
  }
392
394
  DomUtils.getInputValue = getInputValue;
@@ -135,6 +135,13 @@ export declare namespace Utils {
135
135
  * @returns
136
136
  */
137
137
  function getDataChanges(input: object, initData: object, ignoreFields?: string[]): string[];
138
+ /**
139
+ * Get nested value from object
140
+ * @param data Data
141
+ * @param name Field name, support property chain like 'jsonData.logSize'
142
+ * @returns Result
143
+ */
144
+ function getNestedValue(data: object, name: string): any;
138
145
  /**
139
146
  * Get input function or value result
140
147
  * @param input Input function or value
@@ -207,6 +214,13 @@ export declare namespace Utils {
207
214
  * @returns Updated fields
208
215
  */
209
216
  function objectUpdated(objNew: object, objPrev: object, ignoreFields?: string[], strict?: number): string[];
217
+ /**
218
+ * Try to parse JSON input to array
219
+ * @param input JSON input
220
+ * @param checkValue Type check value
221
+ * @returns Result
222
+ */
223
+ function parseJsonArray<T>(input: string, checkValue?: T): T[] | undefined;
210
224
  /**
211
225
  * Parse string (JSON) to specific type, no type conversion
212
226
  * For type conversion, please use DataTypes.convert
@@ -248,6 +262,13 @@ export declare namespace Utils {
248
262
  * @param firstOnly Only convert the first word to upper case
249
263
  */
250
264
  const snakeNameToWord: (name: string, firstOnly?: boolean) => string;
265
+ /**
266
+ * Set nested value to object
267
+ * @param data Data
268
+ * @param name Field name, support property chain like 'jsonData.logSize'
269
+ * @param value Value
270
+ */
271
+ function setNestedValue(data: object, name: string, value: unknown): void;
251
272
  /**
252
273
  * Parse path similar with node.js path.parse
253
274
  * @param path Input path
package/lib/mjs/Utils.js CHANGED
@@ -233,6 +233,36 @@ export var Utils;
233
233
  return changes;
234
234
  }
235
235
  Utils.getDataChanges = getDataChanges;
236
+ /**
237
+ * Get nested value from object
238
+ * @param data Data
239
+ * @param name Field name, support property chain like 'jsonData.logSize'
240
+ * @returns Result
241
+ */
242
+ function getNestedValue(data, name) {
243
+ const properties = name.split('.');
244
+ const len = properties.length;
245
+ if (len === 1) {
246
+ return Reflect.get(data, name);
247
+ }
248
+ else {
249
+ let curr = data;
250
+ for (let i = 0; i < len; i++) {
251
+ const property = properties[i];
252
+ if (i + 1 === len) {
253
+ return Reflect.get(curr, property);
254
+ }
255
+ else {
256
+ let p = Reflect.get(curr, property);
257
+ if (p == null) {
258
+ return undefined;
259
+ }
260
+ curr = p;
261
+ }
262
+ }
263
+ }
264
+ }
265
+ Utils.getNestedValue = getNestedValue;
236
266
  /**
237
267
  * Get input function or value result
238
268
  * @param input Input function or value
@@ -383,6 +413,30 @@ export var Utils;
383
413
  return fields;
384
414
  }
385
415
  Utils.objectUpdated = objectUpdated;
416
+ /**
417
+ * Try to parse JSON input to array
418
+ * @param input JSON input
419
+ * @param checkValue Type check value
420
+ * @returns Result
421
+ */
422
+ function parseJsonArray(input, checkValue) {
423
+ try {
424
+ if (!input.startsWith('['))
425
+ input = `[${input}]`;
426
+ const array = JSON.parse(input);
427
+ const type = typeof checkValue;
428
+ if (Array.isArray(array) &&
429
+ (checkValue == null ||
430
+ !array.some((item) => typeof item !== type))) {
431
+ return array;
432
+ }
433
+ }
434
+ catch (e) {
435
+ console.log('parseJsonArray', input, e);
436
+ }
437
+ return;
438
+ }
439
+ Utils.parseJsonArray = parseJsonArray;
386
440
  /**
387
441
  * Parse string (JSON) to specific type, no type conversion
388
442
  * When return type depends on parameter value, uses function overloading, otherwise uses conditional type
@@ -478,6 +532,36 @@ export var Utils;
478
532
  return -1;
479
533
  return n1 - n2;
480
534
  }
535
+ /**
536
+ * Set nested value to object
537
+ * @param data Data
538
+ * @param name Field name, support property chain like 'jsonData.logSize'
539
+ * @param value Value
540
+ */
541
+ function setNestedValue(data, name, value) {
542
+ const properties = name.split('.');
543
+ const len = properties.length;
544
+ if (len === 1)
545
+ Reflect.set(data, name, value);
546
+ else {
547
+ let curr = data;
548
+ for (let i = 0; i < len; i++) {
549
+ const property = properties[i];
550
+ if (i + 1 === len) {
551
+ Reflect.set(curr, property, value);
552
+ }
553
+ else {
554
+ let p = Reflect.get(curr, property);
555
+ if (p == null) {
556
+ p = {};
557
+ Reflect.set(curr, property, p);
558
+ }
559
+ curr = p;
560
+ }
561
+ }
562
+ }
563
+ }
564
+ Utils.setNestedValue = setNestedValue;
481
565
  /**
482
566
  * Parse path similar with node.js path.parse
483
567
  * @param path Input path
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/shared",
3
- "version": "1.2.13",
3
+ "version": "1.2.15",
4
4
  "description": "TypeScript shared utilities and functions",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
package/src/DomUtils.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /// <reference lib="dom" />
2
2
  import { DataTypes } from './DataTypes';
3
+ import { DateUtils } from './DateUtils';
3
4
  import { FormDataFieldValue, IFormData } from './types/FormData';
4
5
 
5
6
  if (typeof navigator === 'undefined') {
@@ -469,7 +470,7 @@ export namespace DomUtils {
469
470
  if (isNaN(num)) return null;
470
471
  return num;
471
472
  } else if (type === 'date' || type === 'datetime-local')
472
- return input.valueAsDate;
473
+ return input.valueAsDate ?? DateUtils.parse(input.value);
473
474
  return input.value;
474
475
  }
475
476
 
package/src/Utils.ts CHANGED
@@ -352,6 +352,36 @@ export namespace Utils {
352
352
  return changes;
353
353
  }
354
354
 
355
+ /**
356
+ * Get nested value from object
357
+ * @param data Data
358
+ * @param name Field name, support property chain like 'jsonData.logSize'
359
+ * @returns Result
360
+ */
361
+ export function getNestedValue(data: object, name: string) {
362
+ const properties = name.split('.');
363
+ const len = properties.length;
364
+ if (len === 1) {
365
+ return Reflect.get(data, name);
366
+ } else {
367
+ let curr = data;
368
+ for (let i = 0; i < len; i++) {
369
+ const property = properties[i];
370
+
371
+ if (i + 1 === len) {
372
+ return Reflect.get(curr, property);
373
+ } else {
374
+ let p = Reflect.get(curr, property);
375
+ if (p == null) {
376
+ return undefined;
377
+ }
378
+
379
+ curr = p;
380
+ }
381
+ }
382
+ }
383
+ }
384
+
355
385
  /**
356
386
  * Get input function or value result
357
387
  * @param input Input function or value
@@ -541,6 +571,33 @@ export namespace Utils {
541
571
  return fields;
542
572
  }
543
573
 
574
+ /**
575
+ * Try to parse JSON input to array
576
+ * @param input JSON input
577
+ * @param checkValue Type check value
578
+ * @returns Result
579
+ */
580
+ export function parseJsonArray<T>(
581
+ input: string,
582
+ checkValue?: T
583
+ ): T[] | undefined {
584
+ try {
585
+ if (!input.startsWith('[')) input = `[${input}]`;
586
+ const array = JSON.parse(input);
587
+ const type = typeof checkValue;
588
+ if (
589
+ Array.isArray(array) &&
590
+ (checkValue == null ||
591
+ !array.some((item) => typeof item !== type))
592
+ ) {
593
+ return array;
594
+ }
595
+ } catch (e) {
596
+ console.log('parseJsonArray', input, e);
597
+ }
598
+ return;
599
+ }
600
+
544
601
  /**
545
602
  * Parse string (JSON) to specific type, no type conversion
546
603
  * For type conversion, please use DataTypes.convert
@@ -673,6 +730,36 @@ export namespace Utils {
673
730
  return n1 - n2;
674
731
  }
675
732
 
733
+ /**
734
+ * Set nested value to object
735
+ * @param data Data
736
+ * @param name Field name, support property chain like 'jsonData.logSize'
737
+ * @param value Value
738
+ */
739
+ export function setNestedValue(data: object, name: string, value: unknown) {
740
+ const properties = name.split('.');
741
+ const len = properties.length;
742
+ if (len === 1) Reflect.set(data, name, value);
743
+ else {
744
+ let curr = data;
745
+ for (let i = 0; i < len; i++) {
746
+ const property = properties[i];
747
+
748
+ if (i + 1 === len) {
749
+ Reflect.set(curr, property, value);
750
+ } else {
751
+ let p = Reflect.get(curr, property);
752
+ if (p == null) {
753
+ p = {};
754
+ Reflect.set(curr, property, p);
755
+ }
756
+
757
+ curr = p;
758
+ }
759
+ }
760
+ }
761
+ }
762
+
676
763
  /**
677
764
  * Parse path similar with node.js path.parse
678
765
  * @param path Input path