pixel-react 1.0.0 → 1.0.2
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/.yarn/install-state.gz +0 -0
- package/README.md +60 -10
- package/lib/components/MultiSelect/MultiSelect.d.ts +1 -1
- package/lib/components/MultiSelect/MultiSelectTypes.d.ts +2 -0
- package/lib/components/MultiSelect/dropdownTypes.d.ts +2 -0
- package/lib/index.d.ts +3 -1
- package/lib/index.esm.js +514 -2327
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +514 -2328
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/compareArrays/compareArrays.d.ts +11 -0
- package/lib/utils/compareArrays/compareArrays.stories.d.ts +6 -0
- package/lib/utils/compareObjects/compareObjects.d.ts +2 -0
- package/lib/utils/compareObjects/compareObjects.stories.d.ts +6 -0
- package/lib/utils/debounce/debounce.d.ts +6 -0
- package/lib/utils/debounce/debounce.stories.d.ts +6 -0
- package/lib/utils/find/findAndInsert.d.ts +7 -0
- package/lib/utils/find/findAndInsert.stories.d.ts +7 -0
- package/lib/utils/throttle/throttle.d.ts +6 -0
- package/lib/utils/throttle/throttle.stories.d.ts +6 -0
- package/package.json +3 -1
- package/rollup.config.mjs +7 -5
- package/src/assets/icons/filter.svg +5 -0
- package/src/assets/styles/_colors.scss +3 -1
- package/src/components/Icon/iconList.ts +2 -0
- package/src/components/MultiSelect/Dropdown.scss +27 -16
- package/src/components/MultiSelect/Dropdown.tsx +51 -28
- package/src/components/MultiSelect/MultiSelect.stories.tsx +9 -5
- package/src/components/MultiSelect/MultiSelect.tsx +4 -0
- package/src/components/MultiSelect/MultiSelectTypes.ts +2 -0
- package/src/components/MultiSelect/dropdownTypes.ts +2 -0
- package/src/components/Select/components/Dropdown/Dropdown.tsx +7 -3
- package/src/components/Tooltip/Tooltip.scss +0 -1
- package/src/index.ts +2 -5
- package/src/utils/checkEmpty/checkEmpty.stories.tsx +1 -1
- package/src/utils/checkEmpty/checkEmpty.ts +21 -7
- package/src/utils/compareArrays/compareArrays.stories.tsx +62 -0
- package/src/utils/compareArrays/compareArrays.ts +31 -0
- package/src/utils/compareObjects/compareObjects.stories.tsx +51 -0
- package/src/utils/compareObjects/compareObjects.ts +53 -0
- package/src/utils/debounce/debounce.stories.tsx +81 -0
- package/src/utils/debounce/debounce.ts +28 -0
- package/src/utils/find/findAndInsert.stories.tsx +119 -0
- package/src/utils/find/findAndInsert.ts +49 -0
- package/src/utils/throttle/throttle.stories.tsx +100 -0
- package/src/utils/throttle/throttle.ts +33 -0
- package/vite.config.js +1 -17
- package/ui-library.zip +0 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
import { compareArrays } from './compareArrays'; // Adjust the import path as necessary
|
2
|
+
|
3
|
+
export default {
|
4
|
+
title: 'Utils/compareArrays',
|
5
|
+
component: compareArrays,
|
6
|
+
};
|
7
|
+
|
8
|
+
export const Default = () => {
|
9
|
+
const testCases = [
|
10
|
+
{ arr1: [1, 2, 3], arr2: [1, 2, 3], expected: true },
|
11
|
+
{ arr1: [1, 2, 3], arr2: [1, 2, 4], expected: false },
|
12
|
+
{ arr1: [1, { a: 1 }], arr2: [1, { a: 1 }], expected: true },
|
13
|
+
{ arr1: [1, { a: 1 }], arr2: [1, { a: 2 }], expected: false },
|
14
|
+
{ arr1: [1, 2, 3], arr2: [1, 2, 3, 4], expected: false },
|
15
|
+
{ arr1: [], arr2: [], expected: true },
|
16
|
+
{ arr1: [null, undefined], arr2: [null, undefined], expected: true },
|
17
|
+
{ arr1: [null], arr2: [undefined], expected: false },
|
18
|
+
{
|
19
|
+
arr1: [
|
20
|
+
[1, 2],
|
21
|
+
[3, 4],
|
22
|
+
],
|
23
|
+
arr2: [
|
24
|
+
[1, 2],
|
25
|
+
[3, 4],
|
26
|
+
],
|
27
|
+
expected: true,
|
28
|
+
},
|
29
|
+
{
|
30
|
+
arr1: [
|
31
|
+
[1, 2],
|
32
|
+
[3, 4],
|
33
|
+
],
|
34
|
+
arr2: [
|
35
|
+
[1, 2],
|
36
|
+
[4, 3],
|
37
|
+
],
|
38
|
+
expected: false,
|
39
|
+
},
|
40
|
+
];
|
41
|
+
|
42
|
+
return (
|
43
|
+
<div>
|
44
|
+
<h1>
|
45
|
+
<u>compareArrays(arr1, arr2)</u> - Expected / Actual
|
46
|
+
</h1>
|
47
|
+
{testCases.map(({ arr1, arr2, expected }, index) => {
|
48
|
+
const actual = compareArrays(arr1, arr2);
|
49
|
+
return (
|
50
|
+
<div key={index}>
|
51
|
+
<strong>
|
52
|
+
compareArrays({JSON.stringify(arr1)}, {JSON.stringify(arr2)}) -
|
53
|
+
</strong>
|
54
|
+
<span style={{ color: actual === expected ? 'green' : 'red' }}>
|
55
|
+
{` Expected: ${expected}, Actual: ${actual}`}
|
56
|
+
</span>
|
57
|
+
</div>
|
58
|
+
);
|
59
|
+
})}
|
60
|
+
</div>
|
61
|
+
);
|
62
|
+
};
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { compareObjects } from '../compareObjects/compareObjects';
|
2
|
+
|
3
|
+
// Define a type for any object
|
4
|
+
export type AnyObject = Record<string, unknown>;
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Compare two arrays for equality.
|
8
|
+
* This function checks if both arrays contain the same elements in the same order,
|
9
|
+
* including nested structures.
|
10
|
+
*
|
11
|
+
* @param arr1 - The first array to compare.
|
12
|
+
* @param arr2 - The second array to compare.
|
13
|
+
* @returns - A boolean indicating if the arrays are equal.
|
14
|
+
*/
|
15
|
+
export const compareArrays = (arr1: unknown[], arr2: unknown[]): boolean => {
|
16
|
+
// Check if both are arrays
|
17
|
+
if (!Array.isArray(arr1) || !Array.isArray(arr2)) return false;
|
18
|
+
|
19
|
+
// Check if lengths are different
|
20
|
+
if (arr1.length !== arr2.length) return false;
|
21
|
+
|
22
|
+
// Compare each element
|
23
|
+
return arr1.every((element, index) => {
|
24
|
+
const otherElement = arr2[index];
|
25
|
+
// Recursively compare objects or arrays, or check for strict equality
|
26
|
+
return compareObjects(
|
27
|
+
element as AnyObject | null,
|
28
|
+
otherElement as AnyObject | null
|
29
|
+
);
|
30
|
+
});
|
31
|
+
};
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import { compareObjects } from './compareObjects'; // Adjust the import path as necessary
|
2
|
+
|
3
|
+
export default {
|
4
|
+
title: 'Utils/compareObjects',
|
5
|
+
component: compareObjects,
|
6
|
+
};
|
7
|
+
|
8
|
+
export const Default = () => {
|
9
|
+
const testCases = [
|
10
|
+
{
|
11
|
+
obj1: { a: 1, b: [2, { c: 3 }] },
|
12
|
+
obj2: { a: 1, b: [2, { c: 3 }] },
|
13
|
+
expected: true,
|
14
|
+
},
|
15
|
+
{
|
16
|
+
obj1: { a: 1, b: [2, { c: 3 }] },
|
17
|
+
obj2: { a: 1, b: [3, 2] },
|
18
|
+
expected: false,
|
19
|
+
},
|
20
|
+
{
|
21
|
+
obj1: { a: 1, b: { c: 2 } },
|
22
|
+
obj2: { a: 1, b: { c: 2 } },
|
23
|
+
expected: true,
|
24
|
+
},
|
25
|
+
{
|
26
|
+
obj1: { a: 1, b: { c: 2 } },
|
27
|
+
obj2: { a: 1, b: { c: 3 } },
|
28
|
+
expected: false,
|
29
|
+
},
|
30
|
+
{ obj1: null, obj2: null, expected: true },
|
31
|
+
{ obj1: { a: null }, obj2: { a: null }, expected: true },
|
32
|
+
{ obj1: { a: 1 }, obj2: { a: null }, expected: false },
|
33
|
+
{ obj1: {}, obj2: {}, expected: true },
|
34
|
+
{ obj1: { a: 0 }, obj2: { a: 0 }, expected: true },
|
35
|
+
{ obj1: { a: 0 }, obj2: { a: 1 }, expected: false },
|
36
|
+
];
|
37
|
+
|
38
|
+
return (
|
39
|
+
<div>
|
40
|
+
<h1>
|
41
|
+
<u>compareObjects(obj1, obj2)</u> - true / false
|
42
|
+
</h1>
|
43
|
+
{testCases.map(({ obj1, obj2, expected }, index) => (
|
44
|
+
<div key={index}>
|
45
|
+
compareObjects({JSON.stringify(obj1)}, {JSON.stringify(obj2)}) -
|
46
|
+
{compareObjects(obj1, obj2) === expected ? ' True' : ' False'}
|
47
|
+
</div>
|
48
|
+
))}
|
49
|
+
</div>
|
50
|
+
);
|
51
|
+
};
|
@@ -0,0 +1,53 @@
|
|
1
|
+
// Define a type for any object
|
2
|
+
export type AnyObject = Record<string, unknown>;
|
3
|
+
|
4
|
+
export const compareObjects = (
|
5
|
+
obj1: AnyObject | null,
|
6
|
+
obj2: AnyObject | null
|
7
|
+
): boolean => {
|
8
|
+
// Check if both are strictly equal (handles primitive types and same reference)
|
9
|
+
if (obj1 === obj2) return true;
|
10
|
+
|
11
|
+
// Check if either is null or not an object
|
12
|
+
if (
|
13
|
+
obj1 == null ||
|
14
|
+
obj2 == null ||
|
15
|
+
typeof obj1 !== 'object' ||
|
16
|
+
typeof obj2 !== 'object'
|
17
|
+
) {
|
18
|
+
return false;
|
19
|
+
}
|
20
|
+
|
21
|
+
// Handle array comparison
|
22
|
+
const isArray1 = Array.isArray(obj1);
|
23
|
+
const isArray2 = Array.isArray(obj2);
|
24
|
+
if (isArray1 !== isArray2) return false; // One is an array, the other is not
|
25
|
+
|
26
|
+
// Create arrays of keys for both objects
|
27
|
+
const keys1 = isArray1 ? obj1 : Object.keys(obj1);
|
28
|
+
const keys2 = isArray2 ? obj2 : Object.keys(obj2);
|
29
|
+
|
30
|
+
// Check if the number of keys is different
|
31
|
+
if (keys1.length !== keys2.length) return false;
|
32
|
+
|
33
|
+
// Create a Set for keys2 for O(1) lookups (only for objects)
|
34
|
+
if (!isArray1) {
|
35
|
+
const keysSet2 = new Set<string>(keys2 as string[]);
|
36
|
+
|
37
|
+
// Check each key and value
|
38
|
+
return keys1.every((key) => {
|
39
|
+
return (
|
40
|
+
keysSet2.has(key) &&
|
41
|
+
compareObjects(
|
42
|
+
obj1[key] as AnyObject | null,
|
43
|
+
obj2[key] as AnyObject | null
|
44
|
+
)
|
45
|
+
);
|
46
|
+
});
|
47
|
+
} else {
|
48
|
+
// If arrays, compare elements directly
|
49
|
+
return keys1.every((item, index) =>
|
50
|
+
compareObjects(item as AnyObject | null, keys2[index] as AnyObject | null)
|
51
|
+
);
|
52
|
+
}
|
53
|
+
};
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import { debounce } from './debounce'; // Adjust the import path as necessary
|
2
|
+
|
3
|
+
export default {
|
4
|
+
title: 'Utils/debounce',
|
5
|
+
component: debounce,
|
6
|
+
};
|
7
|
+
|
8
|
+
export const Default = () => {
|
9
|
+
const testCases = [
|
10
|
+
{
|
11
|
+
description: 'Basic function call',
|
12
|
+
expectedOutput: 'Function called! (after delay)',
|
13
|
+
delay: 300,
|
14
|
+
setup: () => {
|
15
|
+
const debouncedFunc = debounce(
|
16
|
+
() => console.log('Function called!'),
|
17
|
+
300
|
18
|
+
);
|
19
|
+
debouncedFunc(); // Call the debounced function
|
20
|
+
},
|
21
|
+
code: `const debouncedFunc = debounce(() => console.log('Function called!'), 300);\ndebouncedFunc();`,
|
22
|
+
},
|
23
|
+
{
|
24
|
+
description: 'Rapid calls within delay',
|
25
|
+
expectedOutput: 'Debounced call! (only once after delay)',
|
26
|
+
delay: 500,
|
27
|
+
setup: () => {
|
28
|
+
const debouncedFunc = debounce(
|
29
|
+
() => console.log('Debounced call!'),
|
30
|
+
500
|
31
|
+
);
|
32
|
+
for (let i = 0; i < 5; i++) {
|
33
|
+
debouncedFunc(); // Call the debounced function multiple times
|
34
|
+
}
|
35
|
+
},
|
36
|
+
code: `const debouncedFunc = debounce(() => console.log('Debounced call!'), 500);\nfor (let i = 0; i < 5; i++) {\n debouncedFunc();\n}`,
|
37
|
+
},
|
38
|
+
{
|
39
|
+
description: 'Cancel debounced function',
|
40
|
+
expectedOutput: 'Function should not be called if canceled',
|
41
|
+
delay: 200,
|
42
|
+
setup: () => {
|
43
|
+
const debouncedFunc = debounce(
|
44
|
+
() => console.log('Should not be called!'),
|
45
|
+
200
|
46
|
+
);
|
47
|
+
debouncedFunc(); // Call the debounced function
|
48
|
+
debouncedFunc.cancel(); // Cancel the function
|
49
|
+
},
|
50
|
+
code: `const debouncedFunc = debounce(() => console.log('Should not be called!'), 200);\ndebouncedFunc();\ndebouncedFunc.cancel();`,
|
51
|
+
},
|
52
|
+
];
|
53
|
+
|
54
|
+
return (
|
55
|
+
<div>
|
56
|
+
<h1>
|
57
|
+
<u>debounce(function, delay)</u> - Demonstrating debounce functionality
|
58
|
+
</h1>
|
59
|
+
{testCases.map(
|
60
|
+
({ description, expectedOutput, delay, setup, code }, index) => (
|
61
|
+
<div key={index}>
|
62
|
+
<h3>{description}</h3>
|
63
|
+
<button
|
64
|
+
onClick={() => {
|
65
|
+
setup(); // Run the setup for the test case
|
66
|
+
setTimeout(() => {
|
67
|
+
console.log(expectedOutput);
|
68
|
+
}, delay + 100); // Wait a bit longer than the delay to check output
|
69
|
+
}}
|
70
|
+
>
|
71
|
+
Run Test
|
72
|
+
</button>
|
73
|
+
<pre>
|
74
|
+
<code>{code}</code>
|
75
|
+
</pre>
|
76
|
+
</div>
|
77
|
+
)
|
78
|
+
)}
|
79
|
+
</div>
|
80
|
+
);
|
81
|
+
};
|
@@ -0,0 +1,28 @@
|
|
1
|
+
type Callback = (...args: any[]) => void;
|
2
|
+
|
3
|
+
// Export the DebouncedFunction interface to make it publicly accessible
|
4
|
+
export interface DebouncedFunction extends Function {
|
5
|
+
cancel: () => void;
|
6
|
+
}
|
7
|
+
|
8
|
+
export const debounce = (func: Callback, delay: number): DebouncedFunction => {
|
9
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
10
|
+
|
11
|
+
const debounced: DebouncedFunction = function (this: any, ...args: any[]) {
|
12
|
+
// Clear the previous timeout if it exists
|
13
|
+
if (timeoutId) clearTimeout(timeoutId);
|
14
|
+
|
15
|
+
// Set a new timeout
|
16
|
+
timeoutId = setTimeout(() => {
|
17
|
+
func.apply(this, args);
|
18
|
+
}, delay);
|
19
|
+
};
|
20
|
+
|
21
|
+
// Method to cancel the debounced function
|
22
|
+
debounced.cancel = () => {
|
23
|
+
if (timeoutId) clearTimeout(timeoutId);
|
24
|
+
timeoutId = null;
|
25
|
+
};
|
26
|
+
|
27
|
+
return debounced;
|
28
|
+
};
|
@@ -0,0 +1,119 @@
|
|
1
|
+
// findAndInsert.stories.tsx
|
2
|
+
import { useState } from 'react';
|
3
|
+
import { findAndInsert, AnyObject } from './findAndInsert';
|
4
|
+
|
5
|
+
export default {
|
6
|
+
title: 'Utils/findAndInsert',
|
7
|
+
component: findAndInsert,
|
8
|
+
};
|
9
|
+
|
10
|
+
const initialData: AnyObject[] = [
|
11
|
+
{ id: 1, name: 'Object 1' },
|
12
|
+
{
|
13
|
+
id: 2,
|
14
|
+
name: 'Object 2',
|
15
|
+
children: [
|
16
|
+
{ id: 3, name: 'Child 1', children: [{ id: 5, name: 'SubChild 1' }] },
|
17
|
+
{ id: 4, name: 'Child 2' },
|
18
|
+
],
|
19
|
+
},
|
20
|
+
];
|
21
|
+
|
22
|
+
export const InteractivePlayground = () => {
|
23
|
+
const [data, setData] = useState<AnyObject[]>(initialData);
|
24
|
+
const [selectedId, setSelectedId] = useState<number | null>(null);
|
25
|
+
const [newEntryName, setNewEntryName] = useState<string>('New Object');
|
26
|
+
const [insertPosition, setInsertPosition] = useState<
|
27
|
+
'above' | 'below' | 'replace'
|
28
|
+
>('below');
|
29
|
+
|
30
|
+
const handleInsert = () => {
|
31
|
+
if (selectedId !== null) {
|
32
|
+
const newEntry = { id: Date.now(), name: newEntryName };
|
33
|
+
const result = findAndInsert(
|
34
|
+
data,
|
35
|
+
'id',
|
36
|
+
selectedId,
|
37
|
+
newEntry,
|
38
|
+
insertPosition,
|
39
|
+
'children'
|
40
|
+
);
|
41
|
+
if (result) {
|
42
|
+
setData(result.updatedArray);
|
43
|
+
} else {
|
44
|
+
alert('Item not found in nested structure');
|
45
|
+
}
|
46
|
+
}
|
47
|
+
};
|
48
|
+
|
49
|
+
const generateOptions = (
|
50
|
+
data: AnyObject[],
|
51
|
+
prefix = ''
|
52
|
+
): { id: number; label: string }[] => {
|
53
|
+
const options: { id: number; label: string }[] = [];
|
54
|
+
function recurse(items: AnyObject[], levelPrefix: string) {
|
55
|
+
items.forEach((item) => {
|
56
|
+
options.push({
|
57
|
+
id: item.id,
|
58
|
+
label: `${levelPrefix}${item.name} (ID: ${item.id})`,
|
59
|
+
});
|
60
|
+
if (item.children) recurse(item.children, `${levelPrefix}- `);
|
61
|
+
});
|
62
|
+
}
|
63
|
+
recurse(data, prefix);
|
64
|
+
return options;
|
65
|
+
};
|
66
|
+
|
67
|
+
const options = generateOptions(data);
|
68
|
+
|
69
|
+
return (
|
70
|
+
<div>
|
71
|
+
<h1>Interactive Playground for findAndInsert</h1>
|
72
|
+
<label htmlFor="targetId">Select Object:</label>
|
73
|
+
<select
|
74
|
+
id="targetId"
|
75
|
+
onChange={(e) => setSelectedId(Number(e.target.value))}
|
76
|
+
value={selectedId !== null ? selectedId : ''}
|
77
|
+
>
|
78
|
+
<option value="" disabled>
|
79
|
+
Select an object
|
80
|
+
</option>
|
81
|
+
{options.map((option) => (
|
82
|
+
<option key={option.id} value={option.id}>
|
83
|
+
{option.label}
|
84
|
+
</option>
|
85
|
+
))}
|
86
|
+
</select>
|
87
|
+
|
88
|
+
<div>
|
89
|
+
<label htmlFor="newEntryName">New Entry Name:</label>
|
90
|
+
<input
|
91
|
+
type="text"
|
92
|
+
id="newEntryName"
|
93
|
+
value={newEntryName}
|
94
|
+
onChange={(e) => setNewEntryName(e.target.value)}
|
95
|
+
/>
|
96
|
+
</div>
|
97
|
+
|
98
|
+
<div>
|
99
|
+
<label htmlFor="insertPosition">Insert Position:</label>
|
100
|
+
<select
|
101
|
+
id="insertPosition"
|
102
|
+
value={insertPosition}
|
103
|
+
onChange={(e) =>
|
104
|
+
setInsertPosition(e.target.value as 'above' | 'below' | 'replace')
|
105
|
+
}
|
106
|
+
>
|
107
|
+
<option value="above">Above</option>
|
108
|
+
<option value="below">Below</option>
|
109
|
+
<option value="replace">Replace</option>
|
110
|
+
</select>
|
111
|
+
</div>
|
112
|
+
|
113
|
+
<button onClick={handleInsert}>Insert Entry</button>
|
114
|
+
|
115
|
+
<h2>Current Data:</h2>
|
116
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
117
|
+
</div>
|
118
|
+
);
|
119
|
+
};
|
@@ -0,0 +1,49 @@
|
|
1
|
+
// findAndInsert.ts
|
2
|
+
export type AnyObject = { id: number; [key: string]: any };
|
3
|
+
|
4
|
+
export function findAndInsert<T extends AnyObject>(
|
5
|
+
data: T[],
|
6
|
+
key: keyof T,
|
7
|
+
targetId: number,
|
8
|
+
newEntry: T,
|
9
|
+
insertPosition: 'above' | 'below' | 'replace',
|
10
|
+
childrenKey: string = 'children' // Allow dynamic key for nested children
|
11
|
+
): { updatedArray: T[] } | null {
|
12
|
+
function recursiveSearch(items: T[]): { updatedArray: T[] } | null {
|
13
|
+
for (let i = 0; i < items.length; i++) {
|
14
|
+
const item = items[i];
|
15
|
+
|
16
|
+
// Check if the item matches the target id
|
17
|
+
if (item && item[key] === targetId) {
|
18
|
+
const updatedItems = [...items];
|
19
|
+
|
20
|
+
if (insertPosition === 'replace') {
|
21
|
+
updatedItems[i] = newEntry; // Replace the current item
|
22
|
+
} else {
|
23
|
+
// Insert above or below
|
24
|
+
updatedItems.splice(
|
25
|
+
insertPosition === 'above' ? i : i + 1,
|
26
|
+
0,
|
27
|
+
newEntry
|
28
|
+
);
|
29
|
+
}
|
30
|
+
|
31
|
+
return { updatedArray: updatedItems };
|
32
|
+
}
|
33
|
+
|
34
|
+
// Dynamically check nested children using the `childrenKey`
|
35
|
+
if (item && item[childrenKey] && Array.isArray(item[childrenKey])) {
|
36
|
+
const result = recursiveSearch(item[childrenKey] as T[]);
|
37
|
+
if (result) {
|
38
|
+
return {
|
39
|
+
updatedArray: items.map((x, idx) =>
|
40
|
+
idx === i ? { ...x, [childrenKey]: result.updatedArray } : x
|
41
|
+
),
|
42
|
+
};
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
return null;
|
47
|
+
}
|
48
|
+
return recursiveSearch(data);
|
49
|
+
}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import { throttle } from './throttle'; // Adjust the import path as necessary
|
2
|
+
|
3
|
+
export default {
|
4
|
+
title: 'Utils/throttle',
|
5
|
+
component: throttle,
|
6
|
+
};
|
7
|
+
|
8
|
+
export const Default = () => {
|
9
|
+
const testCases = [
|
10
|
+
{
|
11
|
+
description: 'Basic function call',
|
12
|
+
expectedOutput: 'Function called! (at most once every 300ms)',
|
13
|
+
limit: 300,
|
14
|
+
setup: () => {
|
15
|
+
const throttledFunc = throttle(
|
16
|
+
() => console.log('Function called!'),
|
17
|
+
300
|
18
|
+
);
|
19
|
+
for (let i = 0; i < 5; i++) {
|
20
|
+
throttledFunc(); // Call the throttled function multiple times
|
21
|
+
}
|
22
|
+
},
|
23
|
+
code: `const throttledFunc = throttle(() => console.log('Function called!'), 300);\nfor (let i = 0; i < 5; i++) {\n throttledFunc();\n}`,
|
24
|
+
},
|
25
|
+
{
|
26
|
+
description: 'Throttling with rapid calls',
|
27
|
+
expectedOutput: 'Function called! (at most once every 500ms)',
|
28
|
+
limit: 500,
|
29
|
+
setup: () => {
|
30
|
+
const throttledFunc = throttle(
|
31
|
+
() => console.log('Throttled call!'),
|
32
|
+
500
|
33
|
+
);
|
34
|
+
for (let i = 0; i < 10; i++) {
|
35
|
+
throttledFunc(); // Call the throttled function rapidly
|
36
|
+
}
|
37
|
+
},
|
38
|
+
code: `const throttledFunc = throttle(() => console.log('Throttled call!'), 500);\nfor (let i = 0; i < 10; i++) {\n throttledFunc();\n}`,
|
39
|
+
},
|
40
|
+
{
|
41
|
+
description: 'Cancel throttled function',
|
42
|
+
expectedOutput: 'Function should not be called if canceled',
|
43
|
+
limit: 200,
|
44
|
+
setup: () => {
|
45
|
+
const throttledFunc = throttle(
|
46
|
+
() => console.log('Should not be called!'),
|
47
|
+
200
|
48
|
+
);
|
49
|
+
throttledFunc(); // Call the throttled function
|
50
|
+
throttledFunc.cancel(); // Cancel the function
|
51
|
+
},
|
52
|
+
code: `const throttledFunc = throttle(() => console.log('Should not be called!'), 200);\nthrottledFunc();\nthrottledFunc.cancel();`,
|
53
|
+
},
|
54
|
+
{
|
55
|
+
description: 'Scroll event handling',
|
56
|
+
expectedOutput: 'Scroll event handled (at most once every 400ms)',
|
57
|
+
limit: 400,
|
58
|
+
setup: () => {
|
59
|
+
const throttledScroll = throttle(
|
60
|
+
() => console.log('Scroll event handled'),
|
61
|
+
400
|
62
|
+
);
|
63
|
+
|
64
|
+
// Simulating rapid scroll events
|
65
|
+
for (let i = 0; i < 10; i++) {
|
66
|
+
throttledScroll(); // Simulate scroll event
|
67
|
+
}
|
68
|
+
},
|
69
|
+
code: `const throttledScroll = throttle(() => console.log('Scroll event handled'), 400);\nfor (let i = 0; i < 10; i++) {\n throttledScroll();\n}`,
|
70
|
+
},
|
71
|
+
];
|
72
|
+
|
73
|
+
return (
|
74
|
+
<div>
|
75
|
+
<h1>
|
76
|
+
<u>throttle(function, limit)</u> - Demonstrating throttle functionality
|
77
|
+
</h1>
|
78
|
+
{testCases.map(
|
79
|
+
({ description, expectedOutput, limit, setup, code }, index) => (
|
80
|
+
<div key={index}>
|
81
|
+
<h3>{description}</h3>
|
82
|
+
<button
|
83
|
+
onClick={() => {
|
84
|
+
setup(); // Run the setup for the test case
|
85
|
+
setTimeout(() => {
|
86
|
+
console.log(expectedOutput);
|
87
|
+
}, limit + 100); // Wait a bit longer than the limit to check output
|
88
|
+
}}
|
89
|
+
>
|
90
|
+
Run Test
|
91
|
+
</button>
|
92
|
+
<pre>
|
93
|
+
<code>{code}</code>
|
94
|
+
</pre>
|
95
|
+
</div>
|
96
|
+
)
|
97
|
+
)}
|
98
|
+
</div>
|
99
|
+
);
|
100
|
+
};
|
@@ -0,0 +1,33 @@
|
|
1
|
+
type Callback = (...args: any[]) => void;
|
2
|
+
|
3
|
+
export interface ThrottledFunction extends Function {
|
4
|
+
cancel: () => void;
|
5
|
+
}
|
6
|
+
|
7
|
+
export const throttle = (func: Callback, limit: number): ThrottledFunction => {
|
8
|
+
let lastFunc: ReturnType<typeof setTimeout> | null;
|
9
|
+
let lastRan: number | null = null;
|
10
|
+
|
11
|
+
const throttled: ThrottledFunction = function (this: any, ...args: any[]) {
|
12
|
+
const context = this;
|
13
|
+
if (lastRan === null || Date.now() - lastRan >= limit) {
|
14
|
+
func.apply(context, args);
|
15
|
+
lastRan = Date.now();
|
16
|
+
} else {
|
17
|
+
if (lastFunc) clearTimeout(lastFunc);
|
18
|
+
lastFunc = setTimeout(() => {
|
19
|
+
func.apply(context, args);
|
20
|
+
lastRan = Date.now();
|
21
|
+
}, limit - (Date.now() - lastRan));
|
22
|
+
}
|
23
|
+
};
|
24
|
+
|
25
|
+
// Method to cancel the throttled function
|
26
|
+
throttled.cancel = () => {
|
27
|
+
if (lastFunc) clearTimeout(lastFunc);
|
28
|
+
lastFunc = null;
|
29
|
+
lastRan = null;
|
30
|
+
};
|
31
|
+
|
32
|
+
return throttled;
|
33
|
+
};
|
package/vite.config.js
CHANGED
@@ -4,27 +4,11 @@ import svgrPlugin from 'vite-plugin-svgr';
|
|
4
4
|
|
5
5
|
export default {
|
6
6
|
plugins: [
|
7
|
-
react(),
|
7
|
+
react({ fastRefresh: false }),
|
8
8
|
svgrPlugin({
|
9
9
|
svgrOptions: {
|
10
10
|
icon: true,
|
11
11
|
},
|
12
12
|
}),
|
13
13
|
],
|
14
|
-
|
15
|
-
// resolve: {
|
16
|
-
// alias: {
|
17
|
-
// '@components': path.resolve(__dirname, './src/components'),
|
18
|
-
// '@styles': path.resolve(__dirname, './src/assets/styles'),
|
19
|
-
// '@icons': path.resolve(__dirname, './src/assets/icons'),
|
20
|
-
// },
|
21
|
-
// },
|
22
|
-
|
23
|
-
// css: {
|
24
|
-
// preprocessorOptions: {
|
25
|
-
// scss: {
|
26
|
-
// additionalData: `@import "./src/assets/styles/_colors.scss";`,
|
27
|
-
// },
|
28
|
-
// },
|
29
|
-
// },
|
30
14
|
};
|
package/ui-library.zip
DELETED
Binary file
|