@slickgrid-universal/utils 4.0.2 → 4.2.0
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/dist/cjs/domUtils.js +3 -3
- package/dist/cjs/domUtils.js.map +1 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/nodeExtend.js +125 -0
- package/dist/cjs/nodeExtend.js.map +1 -0
- package/dist/cjs/utils.js +11 -40
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/domUtils.js +3 -3
- package/dist/esm/domUtils.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/nodeExtend.js +121 -0
- package/dist/esm/nodeExtend.js.map +1 -0
- package/dist/esm/utils.js +10 -38
- package/dist/esm/utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/domUtils.d.ts +1 -1
- package/dist/types/domUtils.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/nodeExtend.d.ts +13 -0
- package/dist/types/nodeExtend.d.ts.map +1 -0
- package/dist/types/utils.d.ts +0 -12
- package/dist/types/utils.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/domUtils.ts +249 -0
- package/src/index.ts +5 -0
- package/src/nodeExtend.ts +131 -0
- package/src/stripTagsUtil.ts +193 -0
- package/src/types/htmlElementPosition.interface.ts +6 -0
- package/src/types/index.ts +2 -0
- package/src/types/infer.type.ts +6 -0
- package/src/utils.ts +368 -0
package/src/utils.ts
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add an item to an array only when the item does not exists, when the item is an object we will be using their "id" to compare
|
|
3
|
+
* @param inputArray
|
|
4
|
+
* @param inputItem
|
|
5
|
+
* @param itemIdPropName
|
|
6
|
+
*/
|
|
7
|
+
export function addToArrayWhenNotExists<T = any>(inputArray: T[], inputItem: T, itemIdPropName = 'id') {
|
|
8
|
+
let arrayRowIndex = -1;
|
|
9
|
+
if (inputItem && typeof inputItem === 'object' && itemIdPropName in inputItem) {
|
|
10
|
+
arrayRowIndex = inputArray.findIndex((item) => item[itemIdPropName as keyof T] === inputItem[itemIdPropName as keyof T]);
|
|
11
|
+
} else {
|
|
12
|
+
arrayRowIndex = inputArray.findIndex((item) => item === inputItem);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (arrayRowIndex < 0) {
|
|
16
|
+
inputArray.push(inputItem);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Simple function that will return a string with the number of whitespaces asked, mostly used by the CSV export
|
|
22
|
+
* @param {Number} nbSpaces - number of white spaces to create
|
|
23
|
+
* @param {String} spaceChar - optionally provide a different character to use for the whitespace (e.g. could be override to use " " in html)
|
|
24
|
+
*/
|
|
25
|
+
export function addWhiteSpaces(nbSpaces: number, spaceChar = ' '): string {
|
|
26
|
+
let result = '';
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < nbSpaces; i++) {
|
|
29
|
+
result += spaceChar;
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Remove an item from the array by its index
|
|
36
|
+
* @param array input
|
|
37
|
+
* @param index
|
|
38
|
+
*/
|
|
39
|
+
export function arrayRemoveItemByIndex<T>(array: T[], index: number): T[] {
|
|
40
|
+
return array.filter((_el: T, i: number) => index !== i);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create an immutable clone of an array or object
|
|
45
|
+
* (c) 2019 Chris Ferdinandi, MIT License, https://gomakethings.com
|
|
46
|
+
* @param {Array|Object} objectOrArray - the array or object to copy
|
|
47
|
+
* @return {Array|Object} - the clone of the array or object
|
|
48
|
+
*/
|
|
49
|
+
export function deepCopy(objectOrArray: any | any[]): any | any[] {
|
|
50
|
+
/**
|
|
51
|
+
* Create an immutable copy of an object
|
|
52
|
+
* @return {Object}
|
|
53
|
+
*/
|
|
54
|
+
const cloneObj = () => {
|
|
55
|
+
// Create new object
|
|
56
|
+
const clone = {};
|
|
57
|
+
|
|
58
|
+
// Loop through each item in the original
|
|
59
|
+
// Recursively copy it's value and add to the clone
|
|
60
|
+
Object.keys(objectOrArray).forEach(key => {
|
|
61
|
+
if (Object.prototype.hasOwnProperty.call(objectOrArray, key)) {
|
|
62
|
+
(clone as any)[key] = deepCopy(objectOrArray[key]);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return clone;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Create an immutable copy of an array
|
|
70
|
+
* @return {Array}
|
|
71
|
+
*/
|
|
72
|
+
const cloneArr = () => objectOrArray.map((item: any) => deepCopy(item));
|
|
73
|
+
|
|
74
|
+
// -- init --//
|
|
75
|
+
// Get object type
|
|
76
|
+
const type = Object.prototype.toString.call(objectOrArray).slice(8, -1).toLowerCase();
|
|
77
|
+
|
|
78
|
+
// If an object
|
|
79
|
+
if (type === 'object') {
|
|
80
|
+
return cloneObj();
|
|
81
|
+
}
|
|
82
|
+
// If an array
|
|
83
|
+
if (type === 'array') {
|
|
84
|
+
return cloneArr();
|
|
85
|
+
}
|
|
86
|
+
// Otherwise, return it as-is
|
|
87
|
+
return objectOrArray;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Performs a deep merge of objects and returns new object, it does not modify the source object, objects (immutable) and merges arrays via concatenation.
|
|
92
|
+
* Also, if first argument is undefined/null but next argument is an object then it will proceed and output will be an object
|
|
93
|
+
* @param {Object} target - the target object — what to apply the sources' properties to, which is returned after it is modified.
|
|
94
|
+
* @param {Object} sources - the source object(s) — objects containing the properties you want to apply.
|
|
95
|
+
* @returns {Object} The target object.
|
|
96
|
+
*/
|
|
97
|
+
export function deepMerge(target: any, ...sources: any[]): any {
|
|
98
|
+
if (!sources.length) {
|
|
99
|
+
return target;
|
|
100
|
+
}
|
|
101
|
+
const source = sources.shift();
|
|
102
|
+
|
|
103
|
+
// when target is not an object but source is an object, then we'll assign as object
|
|
104
|
+
target = (!isObject(target) && isObject(source)) ? {} : target;
|
|
105
|
+
|
|
106
|
+
if (isObject(target) && isObject(source)) {
|
|
107
|
+
Object.keys(source).forEach(prop => {
|
|
108
|
+
if (source.hasOwnProperty(prop)) {
|
|
109
|
+
if (prop in target) {
|
|
110
|
+
// handling merging of two properties with equal names
|
|
111
|
+
if (typeof target[prop] !== 'object') {
|
|
112
|
+
target[prop] = source[prop];
|
|
113
|
+
} else {
|
|
114
|
+
if (typeof source[prop] !== 'object') {
|
|
115
|
+
target[prop] = source[prop];
|
|
116
|
+
} else {
|
|
117
|
+
if (target[prop].concat && source[prop].concat) {
|
|
118
|
+
// two arrays get concatenated
|
|
119
|
+
target[prop] = target[prop].concat(source[prop]);
|
|
120
|
+
} else {
|
|
121
|
+
// two objects get merged recursively
|
|
122
|
+
target[prop] = deepMerge(target[prop], source[prop]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
// new properties get added to target
|
|
128
|
+
target[prop] = source[prop];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return deepMerge(target, ...sources);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Empty an object properties by looping through them all and deleting them
|
|
138
|
+
* @param obj - input object
|
|
139
|
+
*/
|
|
140
|
+
export function emptyObject(obj: any) {
|
|
141
|
+
if (isObject(obj)) {
|
|
142
|
+
Object.keys(obj).forEach(key => {
|
|
143
|
+
if (obj.hasOwnProperty(key)) {
|
|
144
|
+
delete obj[key];
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
obj = null;
|
|
149
|
+
obj = {};
|
|
150
|
+
|
|
151
|
+
return obj;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Check if an object is empty
|
|
156
|
+
* @param obj - input object
|
|
157
|
+
* @returns - boolean
|
|
158
|
+
*/
|
|
159
|
+
export function isEmptyObject(obj: any): boolean {
|
|
160
|
+
if (obj === null || obj === undefined) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
return Object.entries(obj).length === 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function isDefined<T>(value: T | undefined | null): value is T {
|
|
167
|
+
return <T>value !== undefined && <T>value !== null && <T>value !== '';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Simple object check.
|
|
172
|
+
* @param item
|
|
173
|
+
* @returns {boolean}
|
|
174
|
+
*/
|
|
175
|
+
export function isObject(item: any) {
|
|
176
|
+
return item !== null && typeof item === 'object' && !Array.isArray(item) && !(item instanceof Date);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Simple check to detect if the value is a primitive type
|
|
181
|
+
* @param val
|
|
182
|
+
* @returns {boolean}
|
|
183
|
+
*/
|
|
184
|
+
export function isPrimitiveValue(val: any) {
|
|
185
|
+
return typeof val === 'boolean' || typeof val === 'number' || typeof val === 'string' || val === null || val === undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function isPrimitiveOrHTML(val: any) {
|
|
189
|
+
return val instanceof HTMLElement || val instanceof DocumentFragment || isPrimitiveValue(val);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Check if a value has any data (undefined, null or empty string will return False...)
|
|
194
|
+
* NOTE: a `false` boolean is consider as having data so it will return True
|
|
195
|
+
*/
|
|
196
|
+
export function hasData(value: any): boolean {
|
|
197
|
+
return value !== undefined && value !== null && value !== '';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Check if input value is a number, by default it won't be a strict checking
|
|
202
|
+
* but optionally we could check for strict equality, for example in strict "3" will return False but without strict it will return True
|
|
203
|
+
* @param value - input value of any type
|
|
204
|
+
* @param strict - when using strict it also check for strict equality, for example in strict "3" would return False but without strict it would return True
|
|
205
|
+
*/
|
|
206
|
+
export function isNumber(value: any, strict = false) {
|
|
207
|
+
if (strict) {
|
|
208
|
+
return (value === null || value === undefined || typeof value === 'string') ? false : !isNaN(value);
|
|
209
|
+
}
|
|
210
|
+
return (value === null || value === undefined || value === '') ? false : !isNaN(+value);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Check if an object is empty, it will also be considered empty when the input is null, undefined or isn't an object */
|
|
214
|
+
export function isObjectEmpty(obj: unknown) {
|
|
215
|
+
return !obj || (obj && typeof obj === 'object' && Object.keys(obj).length === 0);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Parse any input (bool, number, string) and return a boolean or False when not possible */
|
|
219
|
+
export function parseBoolean(input: any): boolean {
|
|
220
|
+
return /(true|1)/i.test(input + '');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Remove any accents from a string by normalizing it
|
|
225
|
+
* @param {String} text - input text
|
|
226
|
+
* @param {Boolean} shouldLowerCase - should we also lowercase the string output?
|
|
227
|
+
* @returns
|
|
228
|
+
*/
|
|
229
|
+
export function removeAccentFromText(text: string, shouldLowerCase = false) {
|
|
230
|
+
const normalizedText = (typeof text.normalize === 'function') ? text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
|
|
231
|
+
return shouldLowerCase ? normalizedText.toLowerCase() : normalizedText;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Set the object value of deeper node from a given dot (.) notation path (e.g.: "user.firstName") */
|
|
235
|
+
export function setDeepValue<T = unknown>(obj: T, path: string | string[], value: any) {
|
|
236
|
+
if (typeof path === 'string') {
|
|
237
|
+
path = path.split('.');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (path.length > 1) {
|
|
241
|
+
const e = path.shift() as keyof T;
|
|
242
|
+
if (obj && e !== undefined) {
|
|
243
|
+
setDeepValue(
|
|
244
|
+
(obj)[e] = (hasData(obj[e]) && (Array.isArray(obj[e]) || Object.prototype.toString.call((obj)[e]) === '[object Object]'))
|
|
245
|
+
? (obj)[e]
|
|
246
|
+
: {} as any,
|
|
247
|
+
path,
|
|
248
|
+
value
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
} else if (obj && path[0]) {
|
|
252
|
+
(obj)[path[0] as keyof T] = value;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Title case (or capitalize) first char of a string, for example "hello world" will become "Hello world"
|
|
258
|
+
* Change the string to be title case on the complete sentence (upper case first char of each word while changing everything else to lower case)
|
|
259
|
+
* @param inputStr
|
|
260
|
+
* @returns string
|
|
261
|
+
*/
|
|
262
|
+
export function titleCase(inputStr: string, shouldTitleCaseEveryWords = false): string {
|
|
263
|
+
if (typeof inputStr === 'string') {
|
|
264
|
+
if (shouldTitleCaseEveryWords) {
|
|
265
|
+
return inputStr.replace(/\w\S*/g, (outputStr) => {
|
|
266
|
+
return outputStr.charAt(0).toUpperCase() + outputStr.substring(1).toLowerCase();
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return inputStr.charAt(0).toUpperCase() + inputStr.slice(1);
|
|
270
|
+
}
|
|
271
|
+
return inputStr;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Converts a string to camel case (camelCase), for example "hello-world" (or "hellow world") will become "helloWorld"
|
|
276
|
+
* @param inputStr the string to convert
|
|
277
|
+
* @return the string in camel case
|
|
278
|
+
*/
|
|
279
|
+
export function toCamelCase(inputStr: string): string {
|
|
280
|
+
if (typeof inputStr === 'string') {
|
|
281
|
+
return inputStr.replace(/(?:^\w|[A-Z]|\b\w|[\s+\-_\/])/g, (match: string, offset: number) => {
|
|
282
|
+
// remove white space or hypens or underscores
|
|
283
|
+
if (/[\s+\-_\/]/.test(match)) {
|
|
284
|
+
return '';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return offset === 0 ? match.toLowerCase() : match.toUpperCase();
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return inputStr;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Converts a string to kebab (hypen) case, for example "helloWorld" will become "hello-world"
|
|
295
|
+
* @param str the string to convert
|
|
296
|
+
* @return the string in kebab case
|
|
297
|
+
*/
|
|
298
|
+
export function toKebabCase(inputStr: string): string {
|
|
299
|
+
if (typeof inputStr === 'string') {
|
|
300
|
+
return toCamelCase(inputStr).replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
301
|
+
}
|
|
302
|
+
return inputStr;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Converts a camelCase or kebab-case string to a sentence case, for example "helloWorld" will become "Hello World" and "hello-world" will become "Hello world"
|
|
307
|
+
* @param str the string to convert
|
|
308
|
+
* @return the string in kebab case
|
|
309
|
+
*/
|
|
310
|
+
export function toSentenceCase(inputStr: string): string {
|
|
311
|
+
if (typeof inputStr === 'string') {
|
|
312
|
+
const result = inputStr.replace(/([A-Z])|(\-)/g, ' $1').replace(/\s+/g, ' ').trim();
|
|
313
|
+
return result.charAt(0).toUpperCase() + result.slice(1);
|
|
314
|
+
}
|
|
315
|
+
return inputStr;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Converts a string from camelCase to snake_case (underscore) case
|
|
320
|
+
* @param str the string to convert
|
|
321
|
+
* @return the string in kebab case
|
|
322
|
+
*/
|
|
323
|
+
export function toSnakeCase(inputStr: string): string {
|
|
324
|
+
if (typeof inputStr === 'string') {
|
|
325
|
+
return toCamelCase(inputStr).replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
326
|
+
}
|
|
327
|
+
return inputStr;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Takes an input array and makes sure the array has unique values by removing duplicates
|
|
332
|
+
* @param array input with possible duplicates
|
|
333
|
+
* @return array output without duplicates
|
|
334
|
+
*/
|
|
335
|
+
export function uniqueArray<T = any>(arr: T[]): T[] {
|
|
336
|
+
if (Array.isArray(arr) && arr.length > 0) {
|
|
337
|
+
return arr.filter((item: T, index: number) => {
|
|
338
|
+
return arr.indexOf(item) >= index;
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return arr;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Takes an input array of objects and makes sure the array has unique object values by removing duplicates
|
|
346
|
+
* it will loop through the array using a property name (or "id" when is not provided) to compare uniqueness
|
|
347
|
+
* @param array input with possible duplicates
|
|
348
|
+
* @param propertyName defaults to "id"
|
|
349
|
+
* @return array output without duplicates
|
|
350
|
+
*/
|
|
351
|
+
export function uniqueObjectArray(arr: any[], propertyName = 'id'): any[] {
|
|
352
|
+
if (Array.isArray(arr) && arr.length > 0) {
|
|
353
|
+
const result = [];
|
|
354
|
+
const map = new Map();
|
|
355
|
+
|
|
356
|
+
for (const item of arr) {
|
|
357
|
+
if (item && !map.has(item[propertyName])) {
|
|
358
|
+
map.set(item[propertyName], true); // set any value to Map
|
|
359
|
+
result.push({
|
|
360
|
+
id: item[propertyName],
|
|
361
|
+
name: item.name
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
return arr;
|
|
368
|
+
}
|