@codeleap/utils 6.3.0 → 6.8.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/array.d.ts +33 -0
- package/dist/array.d.ts.map +1 -0
- package/dist/cloneDeep.d.ts +10 -0
- package/dist/cloneDeep.d.ts.map +1 -0
- package/dist/colors.d.ts +22 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/date.d.ts +13 -0
- package/dist/date.d.ts.map +1 -0
- package/dist/faker.d.ts +23 -0
- package/dist/faker.d.ts.map +1 -0
- package/dist/file.d.ts +14 -0
- package/dist/file.d.ts.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/locale.d.ts +10 -0
- package/dist/locale.d.ts.map +1 -0
- package/dist/misc.d.ts +67 -0
- package/dist/misc.d.ts.map +1 -0
- package/dist/object.d.ts +115 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/react.d.ts +118 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/string.d.ts +43 -0
- package/dist/string.d.ts.map +1 -0
- package/package.json +22 -8
- package/src/array.ts +28 -3
- package/src/cloneDeep.ts +17 -9
- package/src/colors.ts +19 -3
- package/src/date.ts +9 -0
- package/src/faker.ts +13 -0
- package/src/file.ts +8 -0
- package/src/locale.ts +7 -0
- package/src/misc.ts +56 -8
- package/src/object.ts +122 -20
- package/src/react.tsx +51 -16
- package/src/string.ts +37 -2
- package/package.json.bak +0 -31
package/src/react.tsx
CHANGED
|
@@ -2,6 +2,7 @@ import equals from 'deep-equal'
|
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { TypeGuards } from '@codeleap/types'
|
|
4
4
|
|
|
5
|
+
/** Deep structural equality backed by the `deep-equal` package. */
|
|
5
6
|
export const deepEqual = equals
|
|
6
7
|
|
|
7
8
|
type ArePropsEqualOptions<MergedObject> = {
|
|
@@ -9,6 +10,13 @@ type ArePropsEqualOptions<MergedObject> = {
|
|
|
9
10
|
excludeKeys?: string[]
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Checks a specific subset of props for deep equality between two render cycles.
|
|
15
|
+
*
|
|
16
|
+
* `excludeKeys` are deleted from each compared value **in place** before
|
|
17
|
+
* comparison — this mutates the items inside `previous` and `next`.
|
|
18
|
+
* Pass only the keys you want to ignore, and be aware of the side-effect.
|
|
19
|
+
*/
|
|
12
20
|
export function arePropsEqual<A, B>(
|
|
13
21
|
previous: A,
|
|
14
22
|
next: B,
|
|
@@ -17,8 +25,8 @@ export function arePropsEqual<A, B>(
|
|
|
17
25
|
const { check, excludeKeys = [] } = options
|
|
18
26
|
|
|
19
27
|
for (const c of check) {
|
|
20
|
-
const nextItem = next[c as string]
|
|
21
|
-
const prevItem = previous[c as string]
|
|
28
|
+
const nextItem = (next as any)[c as string]
|
|
29
|
+
const prevItem = (previous as any)[c as string]
|
|
22
30
|
|
|
23
31
|
for (const key of excludeKeys) {
|
|
24
32
|
if (nextItem?.[key]) delete nextItem[key]
|
|
@@ -35,20 +43,35 @@ export function arePropsEqual<A, B>(
|
|
|
35
43
|
return true
|
|
36
44
|
}
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Recursively collects a React children tree into a single flat array.
|
|
48
|
+
*
|
|
49
|
+
* Uses `React.Children.toArray` at each level (which assigns stable keys), then
|
|
50
|
+
* follows any `children` prop on the top-level element to continue flattening.
|
|
51
|
+
* Only one level of `props.children` nesting is followed per call — deeply
|
|
52
|
+
* nested component trees (not just wrapper nodes) are not fully unwound.
|
|
53
|
+
*/
|
|
54
|
+
export const flattenChildren = (children: any, flat: React.ReactNode[] = []): React.ReactNode[] => {
|
|
39
55
|
flat = [...flat, ...React.Children.toArray(children)]
|
|
40
56
|
|
|
41
|
-
if (children
|
|
57
|
+
if (children?.props && children.props.children) {
|
|
42
58
|
return flattenChildren(children.props.children, flat)
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
return flat
|
|
46
62
|
}
|
|
47
63
|
|
|
48
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Flattens a children tree and strips the `children` prop from each element's
|
|
66
|
+
* props, returning a serialisable summary of the tree.
|
|
67
|
+
*
|
|
68
|
+
* Intended for introspection and testing — not for re-rendering, since `ref`
|
|
69
|
+
* and event-handler functions are preserved as-is.
|
|
70
|
+
*/
|
|
71
|
+
export const simplifyChildren = (children: any) => {
|
|
49
72
|
const flat = flattenChildren(children)
|
|
50
73
|
|
|
51
|
-
return flat.map(
|
|
74
|
+
return (flat as any[]).map(
|
|
52
75
|
({
|
|
53
76
|
key,
|
|
54
77
|
ref,
|
|
@@ -63,6 +86,18 @@ export const simplifyChildren = children => {
|
|
|
63
86
|
)
|
|
64
87
|
}
|
|
65
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Renders a slot that accepts a component, a props object, or a pre-rendered node.
|
|
91
|
+
*
|
|
92
|
+
* Resolution order:
|
|
93
|
+
* 1. If `ComponentOrProps` is a function → render it as `<ComponentOrProps {...props} />`.
|
|
94
|
+
* 2. If it is `null`, `undefined`, or an empty object → return `null`.
|
|
95
|
+
* 3. If it is already a valid React element → return it as-is.
|
|
96
|
+
* 4. Otherwise treat it as a props object and render `<DefaultComponent {...props} {...ComponentOrProps} />`.
|
|
97
|
+
*
|
|
98
|
+
* This lets call sites pass either a component override, extra props, or nothing,
|
|
99
|
+
* without needing separate conditional rendering logic.
|
|
100
|
+
*/
|
|
66
101
|
export function getRenderedComponent<P = any>(
|
|
67
102
|
ComponentOrProps: React.ComponentType<P> | P | React.ReactNode | null | undefined,
|
|
68
103
|
DefaultComponent: React.ComponentType<P>,
|
|
@@ -71,14 +106,14 @@ export function getRenderedComponent<P = any>(
|
|
|
71
106
|
const _ComponentOrProps = ComponentOrProps as any
|
|
72
107
|
const _DefaultComponent = DefaultComponent as any
|
|
73
108
|
|
|
74
|
-
if (TypeGuards.isNil(ComponentOrProps) || Object.keys(ComponentOrProps).length === 0) {
|
|
75
|
-
return null
|
|
76
|
-
}
|
|
77
|
-
|
|
78
109
|
if (TypeGuards.isFunction(ComponentOrProps)) {
|
|
79
110
|
return <_ComponentOrProps {...props as unknown as P} />
|
|
80
111
|
}
|
|
81
112
|
|
|
113
|
+
if (TypeGuards.isNil(ComponentOrProps) || Object.keys(ComponentOrProps as object).length === 0) {
|
|
114
|
+
return null
|
|
115
|
+
}
|
|
116
|
+
|
|
82
117
|
if (React.isValidElement(ComponentOrProps)) {
|
|
83
118
|
return ComponentOrProps
|
|
84
119
|
}
|
|
@@ -91,10 +126,10 @@ export function getRenderedComponent<P = any>(
|
|
|
91
126
|
/**
|
|
92
127
|
* Memoizes a React functional component to prevent unnecessary re-renders.
|
|
93
128
|
*
|
|
94
|
-
* This function wraps a React functional component using `React.memo`, which provides
|
|
95
|
-
* a mechanism for memoizing the result of rendering the component. By default, it
|
|
129
|
+
* This function wraps a React functional component using `React.memo`, which provides
|
|
130
|
+
* a mechanism for memoizing the result of rendering the component. By default, it
|
|
96
131
|
* uses a comparison function that always returns `true`, indicating that the component
|
|
97
|
-
* should not re-render, regardless of prop changes. This behavior essentially freezes
|
|
132
|
+
* should not re-render, regardless of prop changes. This behavior essentially freezes
|
|
98
133
|
* the component's rendering until explicitly updated.
|
|
99
134
|
*
|
|
100
135
|
* @template P - The type of the component's props.
|
|
@@ -108,7 +143,7 @@ export function memoize<P extends object>(ComponentToMemoize: React.FunctionComp
|
|
|
108
143
|
/**
|
|
109
144
|
* Checks whether a specific property in two sets of props is equal.
|
|
110
145
|
*
|
|
111
|
-
* This function compares the value of a given property (`prop`) between two objects
|
|
146
|
+
* This function compares the value of a given property (`prop`) between two objects
|
|
112
147
|
* (`prevProps` and `nextProps`) using a deep equality check (`equals`).
|
|
113
148
|
*
|
|
114
149
|
* @template P - The type of the props object.
|
|
@@ -128,7 +163,7 @@ export function memoChecker<P>(prop: keyof P, prevProps: P, nextProps: P): boole
|
|
|
128
163
|
* Memoizes a React functional component based on specific props.
|
|
129
164
|
*
|
|
130
165
|
* This function wraps a React functional component using `React.memo` with a custom comparison
|
|
131
|
-
* function. The comparison function checks if the specified properties (passed as `check`)
|
|
166
|
+
* function. The comparison function checks if the specified properties (passed as `check`)
|
|
132
167
|
* remain equal between renders. If all specified properties are equal, the component will not re-render.
|
|
133
168
|
*
|
|
134
169
|
* @template P - The type of the component's props.
|
|
@@ -163,7 +198,7 @@ export function memoChecker<P>(prop: keyof P, prevProps: P, nextProps: P): boole
|
|
|
163
198
|
*/
|
|
164
199
|
export function memoBy<P extends object>(
|
|
165
200
|
ComponentToMemoize: React.FunctionComponent<P>,
|
|
166
|
-
check: keyof P | Array<keyof P
|
|
201
|
+
check: keyof P | Array<keyof P>,
|
|
167
202
|
): React.NamedExoticComponent<P> {
|
|
168
203
|
return React.memo(ComponentToMemoize, (prevProps, nextProps) => {
|
|
169
204
|
const checks = Array.isArray(check) ? check : [check]
|
package/src/string.ts
CHANGED
|
@@ -1,27 +1,55 @@
|
|
|
1
|
+
/** Collapses all newline characters in `text` to single spaces. */
|
|
1
2
|
export function singleLine(text: string) {
|
|
2
3
|
return text?.replace(/\n/g, ' ')
|
|
3
4
|
}
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Round-trips a value through `JSON.stringify` → `JSON.parse`.
|
|
8
|
+
*
|
|
9
|
+
* Strips non-serialisable properties (functions, `undefined`, class instances)
|
|
10
|
+
* and produces a plain-object clone. Throws if the value contains circular
|
|
11
|
+
* references.
|
|
12
|
+
*/
|
|
13
|
+
export function stringiparse(string: string) {
|
|
6
14
|
return JSON.parse(JSON.stringify(string))
|
|
7
15
|
}
|
|
8
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Changes only the first character of `str`.
|
|
19
|
+
*
|
|
20
|
+
* When `reverse` is `true` the first character is lowercased instead of
|
|
21
|
+
* uppercased — useful for converting PascalCase to camelCase.
|
|
22
|
+
*/
|
|
9
23
|
export function capitalize(str: string, reverse = false) {
|
|
10
24
|
if (!str.length) return str
|
|
11
25
|
const firstChar = reverse ? str[0].toLowerCase() : str[0].toUpperCase()
|
|
12
26
|
return firstChar + str.substring(1)
|
|
13
27
|
}
|
|
14
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Returns `true` if `char` is an uppercase Latin or extended Latin character.
|
|
31
|
+
*
|
|
32
|
+
* Covers the Unicode range `U+0080`–`U+024F` in addition to `A–Z`, so
|
|
33
|
+
* accented capitals (e.g. `É`, `Ñ`) are recognised correctly.
|
|
34
|
+
*/
|
|
15
35
|
export function isUppercase(char: string) {
|
|
16
36
|
return /[A-Z]|[\u0080-\u024F]/.test(char) && char.toUpperCase() === char
|
|
17
37
|
}
|
|
18
38
|
|
|
39
|
+
/** Returns `true` when `char` is **not** considered uppercase by {@link isUppercase}. */
|
|
19
40
|
export function isLowercase(char: string) {
|
|
20
41
|
return !isUppercase(char)
|
|
21
42
|
}
|
|
22
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Converts a camelCase or PascalCase identifier to a space-separated, title-cased string.
|
|
46
|
+
*
|
|
47
|
+
* A space is inserted before each uppercase letter that is immediately preceded
|
|
48
|
+
* by a lowercase letter. The first character is always uppercased.
|
|
49
|
+
* Consecutive uppercase runs (e.g. acronyms like "URL") are not split.
|
|
50
|
+
*/
|
|
23
51
|
export function humanizeCamelCase(str: string) {
|
|
24
|
-
const characters = []
|
|
52
|
+
const characters: string[] = []
|
|
25
53
|
let previousCharacter = ''
|
|
26
54
|
str.split('').forEach((char, idx) => {
|
|
27
55
|
if (idx === 0) {
|
|
@@ -40,6 +68,13 @@ export function humanizeCamelCase(str: string) {
|
|
|
40
68
|
return characters.join('')
|
|
41
69
|
}
|
|
42
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Truncates `str` to `maxLen` characters, appending `'...'` when truncation occurs.
|
|
73
|
+
*
|
|
74
|
+
* The three dots count toward `maxLen`, so the returned string never exceeds
|
|
75
|
+
* `maxLen` characters when truncated. Strings already at or below `maxLen` are
|
|
76
|
+
* returned unchanged.
|
|
77
|
+
*/
|
|
43
78
|
export function ellipsis(str: string, maxLen: number) {
|
|
44
79
|
if (str.length - 3 > maxLen) {
|
|
45
80
|
return str.slice(0, maxLen - 3) + '...'
|
package/package.json.bak
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@codeleap/utils",
|
|
3
|
-
"version": "6.3.0",
|
|
4
|
-
"main": "src/index.ts",
|
|
5
|
-
"license": "UNLICENSED",
|
|
6
|
-
"repository": {
|
|
7
|
-
"url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
|
|
8
|
-
"type": "git",
|
|
9
|
-
"directory": "packages/utils"
|
|
10
|
-
},
|
|
11
|
-
"devDependencies": {
|
|
12
|
-
"@codeleap/config": "workspace:*",
|
|
13
|
-
"@codeleap/types": "workspace:*",
|
|
14
|
-
"ts-node-dev": "1.1.8"
|
|
15
|
-
},
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "echo 'No build needed'"
|
|
18
|
-
},
|
|
19
|
-
"peerDependencies": {
|
|
20
|
-
"@codeleap/types": "workspace:*",
|
|
21
|
-
"axios": "^1.7.9",
|
|
22
|
-
"dayjs": "1.11.18",
|
|
23
|
-
"typescript": "5.5.2",
|
|
24
|
-
"react": "19.1.0",
|
|
25
|
-
"@tanstack/react-query": "5.89.0"
|
|
26
|
-
},
|
|
27
|
-
"dependencies": {
|
|
28
|
-
"tinycolor2": "^1.4.2",
|
|
29
|
-
"deep-equal": "^2.0.5"
|
|
30
|
-
}
|
|
31
|
-
}
|