@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 CHANGED
@@ -1,16 +1,9 @@
1
1
  {
2
2
  "name": "@hrnec06/react_utils",
3
- "version": "1.1.0",
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
- "dist"
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
+ }