@hrnec06/react_utils 1.1.0 → 1.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/package.json +3 -11
- package/src/debugger/DebugParser.tsx +401 -0
- package/src/debugger/Debugger.tsx +297 -0
- package/src/debugger/DebuggerContext.ts +41 -0
- package/src/debugger/DebuggerLogic.ts +30 -0
- package/src/debugger/DebuggerScrollBar.tsx +108 -0
- package/src/debugger/DebuggerSymbols.tsx +63 -0
- package/src/debugger/DebuggerWindowResize.tsx +136 -0
- package/src/hooks/useKeyListener.ts +47 -0
- package/src/hooks/useListener.ts +11 -0
- package/src/hooks/useSignal.ts +31 -0
- package/src/hooks/useUpdateEffect.ts +14 -0
- package/src/hooks/useUpdatedRef.ts +12 -0
- package/src/hooks/useWindowSize.ts +22 -0
- package/src/index.tsx +17 -0
- package/dist/index.d.mts +0 -40
- package/dist/index.d.ts +0 -40
- package/dist/index.js +0 -934
- package/dist/index.mjs +0 -891
package/package.json
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hrnec06/react_utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A debugger component for react.",
|
|
5
5
|
"exports": {
|
|
6
|
-
".":
|
|
7
|
-
"types": {
|
|
8
|
-
"require": "./dist/index.d.ts",
|
|
9
|
-
"import": "./dist/index.d.mts"
|
|
10
|
-
},
|
|
11
|
-
"require": "./dist/index.js",
|
|
12
|
-
"import": "./dist/index.mjs"
|
|
13
|
-
}
|
|
6
|
+
".": "./src/index.tsx"
|
|
14
7
|
},
|
|
15
8
|
"keywords": [],
|
|
16
9
|
"author": {
|
|
@@ -20,7 +13,7 @@
|
|
|
20
13
|
},
|
|
21
14
|
"license": "ISC",
|
|
22
15
|
"files": [
|
|
23
|
-
"
|
|
16
|
+
"src"
|
|
24
17
|
],
|
|
25
18
|
"publishConfig": {
|
|
26
19
|
"access": "public",
|
|
@@ -38,7 +31,6 @@
|
|
|
38
31
|
"react": "^19.1.0"
|
|
39
32
|
},
|
|
40
33
|
"scripts": {
|
|
41
|
-
"build": "tsup src/index.tsx --format cjs,esm --dts",
|
|
42
34
|
"dev": "tsup src/index.tsx --format cjs,esm --dts --watch"
|
|
43
35
|
}
|
|
44
36
|
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { useLayoutEffect, useMemo, useState } from "react";
|
|
2
|
+
import useDebugger from "./DebuggerContext";
|
|
3
|
+
import { matchPath } from "./DebuggerLogic";
|
|
4
|
+
import { entries, keys, padString } from "@hrnec06/util";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { Char_Bracket, Char_Colon, Char_Comma, Chevron_Toggle } from "./DebuggerSymbols";
|
|
7
|
+
|
|
8
|
+
interface ParseValueProps {
|
|
9
|
+
value: unknown,
|
|
10
|
+
path?: string[]
|
|
11
|
+
}
|
|
12
|
+
export default function ParseValue({
|
|
13
|
+
value,
|
|
14
|
+
path = []
|
|
15
|
+
}: ParseValueProps) {
|
|
16
|
+
const debug = useDebugger();
|
|
17
|
+
|
|
18
|
+
switch (typeof value) {
|
|
19
|
+
case 'string': return (<Value_String value={value} />);
|
|
20
|
+
case 'bigint':
|
|
21
|
+
case 'number': return (<Value_Number value={value} />);
|
|
22
|
+
case 'boolean': return (<Value_Boolean value={value} />);
|
|
23
|
+
case 'undefined': return (<Value_Keyword value={"undefined"} />);
|
|
24
|
+
case 'function': return (<Value_Function value={value} />);
|
|
25
|
+
case 'symbol': return (<Value_Symbol value={value} />)
|
|
26
|
+
case 'object': {
|
|
27
|
+
if (value === null)
|
|
28
|
+
return (<Value_Keyword value={"null"} />)
|
|
29
|
+
|
|
30
|
+
const isRootObject = path.length === 0;
|
|
31
|
+
|
|
32
|
+
const shouldBeExpanded = (isRootObject && debug.options.openRoot) || (matchPath(path, debug.paths.open) && !matchPath(path, debug.paths.exclude));
|
|
33
|
+
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
return <Value_Array value={value} path={path} defaultExpanded={shouldBeExpanded} />
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return <Value_Object value={value} path={path} defaultExpanded={shouldBeExpanded} />
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface Value_String_Props {
|
|
44
|
+
value: string,
|
|
45
|
+
noEncase?: boolean
|
|
46
|
+
}
|
|
47
|
+
function Value_String({
|
|
48
|
+
value,
|
|
49
|
+
noEncase = false
|
|
50
|
+
}: Value_String_Props) {
|
|
51
|
+
return (
|
|
52
|
+
<span className="text-[#ce9178]">
|
|
53
|
+
{padString(value, noEncase ? 0 : 1, '"')}
|
|
54
|
+
</span>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface Value_Number_Props {
|
|
59
|
+
value: number | bigint
|
|
60
|
+
}
|
|
61
|
+
function Value_Number({
|
|
62
|
+
value
|
|
63
|
+
}: Value_Number_Props) {
|
|
64
|
+
return (
|
|
65
|
+
<span className="text-[#a7ce9b]">
|
|
66
|
+
{value}
|
|
67
|
+
</span>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface Value_Boolean_Props {
|
|
72
|
+
value: boolean
|
|
73
|
+
}
|
|
74
|
+
function Value_Boolean({
|
|
75
|
+
value
|
|
76
|
+
}: Value_Boolean_Props) {
|
|
77
|
+
return <Value_Keyword value={value ? 'true' : 'false'} />
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface Value_Function_Props {
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
82
|
+
value: Function
|
|
83
|
+
}
|
|
84
|
+
function Value_Function({
|
|
85
|
+
value
|
|
86
|
+
}: Value_Function_Props) {
|
|
87
|
+
const argsMatch = value.toString().match(/^[^(]*\(\s*([^)]*)\)/);
|
|
88
|
+
let args: string = '';
|
|
89
|
+
|
|
90
|
+
if (argsMatch && argsMatch.length > 1) {
|
|
91
|
+
args = argsMatch[1]!;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<>
|
|
96
|
+
<span className="text-[#f2824a]">
|
|
97
|
+
{'ƒ '}
|
|
98
|
+
</span>
|
|
99
|
+
<span>
|
|
100
|
+
{`${value.name}(${args})`}
|
|
101
|
+
</span>
|
|
102
|
+
</>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface Value_Keyword_Props {
|
|
107
|
+
value: string
|
|
108
|
+
}
|
|
109
|
+
function Value_Keyword({
|
|
110
|
+
value
|
|
111
|
+
}: Value_Keyword_Props) {
|
|
112
|
+
return (
|
|
113
|
+
<span className="text-[#439ccb]">
|
|
114
|
+
{value}
|
|
115
|
+
</span>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface Value_Object_Props {
|
|
120
|
+
value: object,
|
|
121
|
+
path: string[],
|
|
122
|
+
defaultExpanded?: boolean
|
|
123
|
+
}
|
|
124
|
+
function Value_Object({
|
|
125
|
+
value,
|
|
126
|
+
path,
|
|
127
|
+
defaultExpanded = false
|
|
128
|
+
}: Value_Object_Props) {
|
|
129
|
+
const debug = useDebugger();
|
|
130
|
+
|
|
131
|
+
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
132
|
+
|
|
133
|
+
const objectEntries = entries(value);
|
|
134
|
+
|
|
135
|
+
const collapsedPreview = useMemo(() => {
|
|
136
|
+
const children: React.ReactNode[] = [];
|
|
137
|
+
const keyList = keys(value);
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < Math.min(keyList.length, 6); i++) {
|
|
140
|
+
children.push(
|
|
141
|
+
<React.Fragment key={i}>
|
|
142
|
+
<Value_Object_Key text={keyList[i]!} />
|
|
143
|
+
{i < keyList.length - 1 && <Char_Comma />}
|
|
144
|
+
</React.Fragment>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (keyList.length > 6)
|
|
149
|
+
children.push(<span key={'rest'} className="text-zinc-500">...</span>);
|
|
150
|
+
|
|
151
|
+
return children;
|
|
152
|
+
}, [value]);
|
|
153
|
+
|
|
154
|
+
useLayoutEffect(() => {
|
|
155
|
+
debug.event.expand(path, expanded);
|
|
156
|
+
}, [expanded]);
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
const handleExpand = (state: boolean) => {
|
|
160
|
+
setExpanded(state);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<>
|
|
165
|
+
<Chevron_Toggle expanded={expanded} onToggle={handleExpand} />
|
|
166
|
+
|
|
167
|
+
{
|
|
168
|
+
Object.getPrototypeOf(value) !== Object.prototype && <span>({value.constructor.name}) </span>
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
{
|
|
172
|
+
expanded ? (
|
|
173
|
+
<>
|
|
174
|
+
<Char_Bracket text="{" />
|
|
175
|
+
|
|
176
|
+
<ul className="pl-4 ml-1 border-l border-l-primary-400">
|
|
177
|
+
{
|
|
178
|
+
objectEntries.map(([key, value], ix) => {
|
|
179
|
+
return (
|
|
180
|
+
<li key={ix}>
|
|
181
|
+
<Value_Object_Key text={key} />
|
|
182
|
+
<Char_Colon />
|
|
183
|
+
|
|
184
|
+
<ParseValue value={value} path={[...path, `${key}`]} />
|
|
185
|
+
|
|
186
|
+
{ix < objectEntries.length - 1 && (
|
|
187
|
+
<Char_Comma />
|
|
188
|
+
)}
|
|
189
|
+
</li>
|
|
190
|
+
);
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
</ul>
|
|
194
|
+
|
|
195
|
+
<Char_Bracket text="}" />
|
|
196
|
+
</>
|
|
197
|
+
) : (
|
|
198
|
+
<div
|
|
199
|
+
className="inline-block cursor-pointer"
|
|
200
|
+
onClick={() => setExpanded(true)}
|
|
201
|
+
>
|
|
202
|
+
<Char_Bracket text="{" />
|
|
203
|
+
{' '}
|
|
204
|
+
{collapsedPreview}
|
|
205
|
+
{' '}
|
|
206
|
+
<Char_Bracket text="}" />
|
|
207
|
+
</div>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
</>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
interface Value_Array_Props {
|
|
215
|
+
value: unknown[],
|
|
216
|
+
path: string[],
|
|
217
|
+
defaultExpanded?: boolean
|
|
218
|
+
}
|
|
219
|
+
function Value_Array({
|
|
220
|
+
value,
|
|
221
|
+
path,
|
|
222
|
+
defaultExpanded = false
|
|
223
|
+
}: Value_Array_Props) {
|
|
224
|
+
const debug = useDebugger();
|
|
225
|
+
|
|
226
|
+
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
227
|
+
|
|
228
|
+
const children = useMemo(() => {
|
|
229
|
+
const children: React.ReactNode[] = [];
|
|
230
|
+
|
|
231
|
+
let ix = 0;
|
|
232
|
+
|
|
233
|
+
while (ix < value.length) {
|
|
234
|
+
const nextTarget = Math.min(ix + debug.options.compactArrays, value.length);
|
|
235
|
+
|
|
236
|
+
children.push(
|
|
237
|
+
<li key={ix}>
|
|
238
|
+
{
|
|
239
|
+
(() => {
|
|
240
|
+
const children: React.ReactNode[] = [];
|
|
241
|
+
|
|
242
|
+
for (;ix < nextTarget; ix++) {
|
|
243
|
+
children.push(
|
|
244
|
+
<React.Fragment key={ix}>
|
|
245
|
+
<ParseValue value={value[ix]} path={[...path, `${ix}`]} />
|
|
246
|
+
{ix < value.length - 1 && (
|
|
247
|
+
<Char_Comma />
|
|
248
|
+
)}
|
|
249
|
+
</React.Fragment>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return children;
|
|
254
|
+
})()
|
|
255
|
+
}
|
|
256
|
+
</li>
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
ix = nextTarget;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return children;
|
|
263
|
+
}, [debug.options.compactArrays, value]);
|
|
264
|
+
|
|
265
|
+
const collapsedPreview = useMemo(() => {
|
|
266
|
+
const children: React.ReactNode[] = [];
|
|
267
|
+
|
|
268
|
+
for (let i = 0; i < Math.min(value.length, 6); i++) {
|
|
269
|
+
children.push(
|
|
270
|
+
<React.Fragment key={i}>
|
|
271
|
+
<ParseValue_ToSimple value={value[i]} />
|
|
272
|
+
|
|
273
|
+
{i < value.length - 1 && <Char_Comma />}
|
|
274
|
+
</React.Fragment>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (value.length > 6)
|
|
279
|
+
children.push(<span key={'rest'} className="text-zinc-500">...</span>);
|
|
280
|
+
|
|
281
|
+
return children;
|
|
282
|
+
}, [value]);
|
|
283
|
+
|
|
284
|
+
useLayoutEffect(() => {
|
|
285
|
+
debug.event.expand(path, expanded);
|
|
286
|
+
}, [expanded]);
|
|
287
|
+
|
|
288
|
+
const handleExpand = (state: boolean) => {
|
|
289
|
+
setExpanded(state);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
<>
|
|
294
|
+
<Chevron_Toggle expanded={expanded} onToggle={handleExpand} />
|
|
295
|
+
|
|
296
|
+
<span>{`(${value.length}) `}</span>
|
|
297
|
+
{
|
|
298
|
+
expanded ? (
|
|
299
|
+
<>
|
|
300
|
+
<Char_Bracket text="[" />
|
|
301
|
+
|
|
302
|
+
<ul className="pl-4 ml-1 border-l border-l-primary-400">
|
|
303
|
+
{children}
|
|
304
|
+
</ul>
|
|
305
|
+
|
|
306
|
+
<Char_Bracket text="]" />
|
|
307
|
+
</>
|
|
308
|
+
) : (
|
|
309
|
+
<div
|
|
310
|
+
className="inline-block cursor-pointer"
|
|
311
|
+
onClick={() => setExpanded(true)}
|
|
312
|
+
>
|
|
313
|
+
<Char_Bracket text="[" />
|
|
314
|
+
{/* <span className="text-zinc-500">...</span> */}
|
|
315
|
+
{collapsedPreview}
|
|
316
|
+
<Char_Bracket text="]" />
|
|
317
|
+
</div>
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
</>
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
interface Value_Object_Key_Props {
|
|
326
|
+
text: string,
|
|
327
|
+
}
|
|
328
|
+
function Value_Object_Key({
|
|
329
|
+
text
|
|
330
|
+
}: Value_Object_Key_Props) {
|
|
331
|
+
return (
|
|
332
|
+
<span className="text-[#9CDCF0]">
|
|
333
|
+
{text}
|
|
334
|
+
</span>
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
interface Value_SymbolProps {
|
|
339
|
+
value: symbol
|
|
340
|
+
}
|
|
341
|
+
function Value_Symbol({
|
|
342
|
+
value
|
|
343
|
+
}: Value_SymbolProps) {
|
|
344
|
+
return (
|
|
345
|
+
<>
|
|
346
|
+
<Value_Keyword value="Symbol" />
|
|
347
|
+
<Char_Bracket text="(" />
|
|
348
|
+
<span>
|
|
349
|
+
{value.description}
|
|
350
|
+
</span>
|
|
351
|
+
<Char_Bracket text=")" />
|
|
352
|
+
</>
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
interface ParseValue_ToSimpleProps {
|
|
357
|
+
value: unknown
|
|
358
|
+
}
|
|
359
|
+
function ParseValue_ToSimple({
|
|
360
|
+
value
|
|
361
|
+
}: ParseValue_ToSimpleProps): React.ReactNode {
|
|
362
|
+
const MAX_LENGTH = 16;
|
|
363
|
+
|
|
364
|
+
switch (typeof value) {
|
|
365
|
+
case 'bigint':
|
|
366
|
+
case 'number': {
|
|
367
|
+
const numStr = value.toString();
|
|
368
|
+
|
|
369
|
+
if (numStr.length > MAX_LENGTH)
|
|
370
|
+
return <Value_Keyword value={typeof value} />;
|
|
371
|
+
|
|
372
|
+
return <Value_Number value={value} />
|
|
373
|
+
}
|
|
374
|
+
case 'boolean': return <Value_Boolean value={value} />
|
|
375
|
+
case 'function': return <Value_Function value={value} />
|
|
376
|
+
case 'object': {
|
|
377
|
+
if (value === null)
|
|
378
|
+
return <Value_Keyword value="null" />
|
|
379
|
+
|
|
380
|
+
const className = Object.getPrototypeOf(value) !== Object.prototype ? value.constructor.name : null;
|
|
381
|
+
|
|
382
|
+
if (className === null || className.length > MAX_LENGTH)
|
|
383
|
+
return <Value_Keyword value="object" />
|
|
384
|
+
|
|
385
|
+
return className;
|
|
386
|
+
}
|
|
387
|
+
case 'string': {
|
|
388
|
+
if (value.length > MAX_LENGTH)
|
|
389
|
+
return <Value_String value="string" noEncase />
|
|
390
|
+
|
|
391
|
+
return <Value_String value={value} />;
|
|
392
|
+
}
|
|
393
|
+
case 'symbol': {
|
|
394
|
+
if (!value.description || value.description.length > MAX_LENGTH)
|
|
395
|
+
return <Value_Keyword value="Symbol" />
|
|
396
|
+
|
|
397
|
+
return <Value_Symbol value={value} />
|
|
398
|
+
}
|
|
399
|
+
case 'undefined': return <Value_Keyword value="undefined" />
|
|
400
|
+
}
|
|
401
|
+
}
|