@hrnec06/react_utils 1.6.0 → 1.7.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 +1 -1
- package/src/components/ContextMenu/ContextMenu.tsx +235 -0
- package/src/components/ContextMenu/ContextMenu.types.ts +17 -0
- package/src/components/ContextMenu/ContextMenuCtx.tsx +33 -0
- package/src/components/ContextMenu/ContextMenuItem.tsx +147 -0
- package/src/components/ContextMenu/ContextMenuRoot.tsx +28 -0
- package/src/components/ContextMenu/ContextMenuSection.tsx +28 -0
- package/src/components/Debugger/Debugger.tsx +70 -18
- package/src/components/Debugger/DebuggerTerminal.tsx +3 -3
- package/src/components/Debugger/parser/DebugParser.tsx +121 -14
- package/src/components/Debugger/parser/DebugTerminal.tsx +24 -7
- package/src/components/Debugger/parser/ValueArray.tsx +22 -7
- package/src/components/Debugger/parser/ValueBoolean.tsx +15 -4
- package/src/components/Debugger/parser/ValueConstant.tsx +21 -0
- package/src/components/Debugger/parser/ValueFunction.tsx +17 -5
- package/src/components/Debugger/parser/ValueNumber.tsx +14 -4
- package/src/components/Debugger/parser/ValueObject.tsx +61 -17
- package/src/components/Debugger/parser/ValueString.tsx +13 -4
- package/src/components/ResizeableBox/ResizeableBox.tsx +1 -1
- package/src/hooks/useDebounce.ts +26 -0
- package/src/hooks/useEvent.ts +15 -0
- package/src/hooks/useLatestRef.ts +12 -0
- package/src/hooks/useSyncRef.ts +17 -0
- package/src/hooks/useTransition.ts +2 -1
- package/src/index.ts +12 -3
- package/src/lib/errors/ContextError.ts +11 -0
- package/src/hooks/useUpdatedRef.ts +0 -12
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { forwardRef, RefObject, useDeferredValue, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
1
2
|
import useDebugger from "../DebuggerContext";
|
|
2
3
|
import { matchPath } from "../DebuggerLogic";
|
|
3
4
|
import { Terminal } from "../DebuggerTerminal";
|
|
@@ -5,46 +6,152 @@ import DebugTerminal from "./DebugTerminal";
|
|
|
5
6
|
import ValueArray from "./ValueArray";
|
|
6
7
|
import ValueBoolean from "./ValueBoolean";
|
|
7
8
|
import ValueFunction from "./ValueFunction";
|
|
8
|
-
import ValueKeyword from "./ValueKeyword";
|
|
9
9
|
import ValueNumber from "./ValueNumber";
|
|
10
10
|
import ValueObject from "./ValueObject";
|
|
11
11
|
import ValueString from "./ValueString";
|
|
12
12
|
import ValueSymbol from "./ValueSymbol";
|
|
13
|
+
import { Nullable } from "@hrnec06/util";
|
|
14
|
+
import ContextMenu from "../../ContextMenu/ContextMenu";
|
|
15
|
+
import ValueConstant from "./ValueConstant";
|
|
16
|
+
import useSyncRef from "../../../hooks/useSyncRef";
|
|
17
|
+
import CTXM from "../../ContextMenu/ContextMenu.types";
|
|
18
|
+
import clsx from "clsx";
|
|
19
|
+
import useDebounce from "../../../hooks/useDebounce";
|
|
20
|
+
import { useCTXMListener } from "../../ContextMenu/ContextMenuCtx";
|
|
13
21
|
|
|
14
|
-
interface
|
|
22
|
+
export interface ParseValueRef {
|
|
23
|
+
copyValue: string,
|
|
24
|
+
name?: string,
|
|
25
|
+
menu?: CTXM.ContextMenuSection,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ClassifyProps {
|
|
29
|
+
ref: React.Ref<ParseValueRef>,
|
|
15
30
|
value: unknown,
|
|
16
31
|
path?: string[]
|
|
17
32
|
}
|
|
18
|
-
|
|
33
|
+
function Classify({
|
|
34
|
+
ref,
|
|
19
35
|
value,
|
|
20
36
|
path = []
|
|
21
|
-
}:
|
|
37
|
+
}: ClassifyProps) {
|
|
22
38
|
const debug = useDebugger();
|
|
23
39
|
|
|
24
40
|
switch (typeof value) {
|
|
25
|
-
case 'string': return (<ValueString value={value} />);
|
|
41
|
+
case 'string': return (<ValueString ref={ref} value={value} />);
|
|
26
42
|
case 'bigint':
|
|
27
|
-
case 'number': return (<ValueNumber value={value} />);
|
|
28
|
-
case 'boolean': return (<ValueBoolean value={value} />);
|
|
29
|
-
case 'undefined': return (<
|
|
30
|
-
case 'function': return (<ValueFunction value={value} />);
|
|
43
|
+
case 'number': return (<ValueNumber ref={ref} value={value} />);
|
|
44
|
+
case 'boolean': return (<ValueBoolean ref={ref} value={value} />);
|
|
45
|
+
case 'undefined': return (<ValueConstant ref={ref} value="undefined" />)
|
|
46
|
+
case 'function': return (<ValueFunction ref={ref} value={value} />);
|
|
31
47
|
case 'symbol': return (<ValueSymbol value={value} />)
|
|
32
48
|
case 'object': {
|
|
33
49
|
if (value === null)
|
|
34
|
-
return (<
|
|
50
|
+
return (<ValueConstant ref={ref} value={"null"} />)
|
|
35
51
|
|
|
36
52
|
const isRootObject = path.length === 0;
|
|
37
53
|
|
|
38
54
|
const shouldBeExpanded = (isRootObject && debug.options.openRoot) || (matchPath(path, debug.paths.open) && !matchPath(path, debug.paths.exclude));
|
|
39
55
|
|
|
40
56
|
if (value instanceof Terminal)
|
|
41
|
-
return <DebugTerminal terminal={value} path={path} defaultExpanded={shouldBeExpanded} />
|
|
57
|
+
return <DebugTerminal ref={ref} terminal={value} path={path} defaultExpanded={shouldBeExpanded} />
|
|
42
58
|
|
|
43
59
|
if (Array.isArray(value)) {
|
|
44
|
-
return <ValueArray value={value} path={path} defaultExpanded={shouldBeExpanded} />
|
|
60
|
+
return <ValueArray ref={ref} value={value} path={path} defaultExpanded={shouldBeExpanded} />
|
|
45
61
|
}
|
|
46
62
|
|
|
47
|
-
return <ValueObject value={value} path={path} defaultExpanded={shouldBeExpanded} />
|
|
63
|
+
return <ValueObject ref={ref} value={value} path={path} defaultExpanded={shouldBeExpanded} />
|
|
48
64
|
}
|
|
49
65
|
}
|
|
50
|
-
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface ParseValueProps {
|
|
69
|
+
value: unknown,
|
|
70
|
+
path?: string[],
|
|
71
|
+
/**
|
|
72
|
+
* Children to be put before the parsed value
|
|
73
|
+
*/
|
|
74
|
+
children?: React.ReactNode
|
|
75
|
+
}
|
|
76
|
+
function ParseValue({
|
|
77
|
+
value,
|
|
78
|
+
path = [],
|
|
79
|
+
children
|
|
80
|
+
}: ParseValueProps)
|
|
81
|
+
{
|
|
82
|
+
const valueRef = useRef<ParseValueRef>(null);
|
|
83
|
+
const [localValue, setLocalValue] = useState<Nullable<ParseValueRef>>(null);
|
|
84
|
+
const syncedValueRef = useSyncRef(valueRef, setLocalValue);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<DebugCTXMProvider
|
|
88
|
+
path={path}
|
|
89
|
+
ref={localValue}
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
|
|
93
|
+
<Classify
|
|
94
|
+
ref={syncedValueRef}
|
|
95
|
+
value={value}
|
|
96
|
+
path={path}
|
|
97
|
+
/>
|
|
98
|
+
</DebugCTXMProvider>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface DebugCTXMProviderProps extends React.PropsWithChildren {
|
|
103
|
+
path: string[],
|
|
104
|
+
ref: Nullable<ParseValueRef>
|
|
105
|
+
}
|
|
106
|
+
export function DebugCTXMProvider({
|
|
107
|
+
path,
|
|
108
|
+
ref,
|
|
109
|
+
children
|
|
110
|
+
}: DebugCTXMProviderProps)
|
|
111
|
+
{
|
|
112
|
+
const ctxmLabel = useMemo(() => {
|
|
113
|
+
if (!path.length)
|
|
114
|
+
return "root";
|
|
115
|
+
|
|
116
|
+
return path.join('.');
|
|
117
|
+
}, [path]);
|
|
118
|
+
|
|
119
|
+
const handleCopy = ref === null
|
|
120
|
+
? undefined
|
|
121
|
+
: () => {
|
|
122
|
+
if (!ref) return;
|
|
123
|
+
|
|
124
|
+
navigator.clipboard.writeText(ref.copyValue);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<ContextMenu
|
|
129
|
+
className={clsx(
|
|
130
|
+
"inline align-middle h-full",
|
|
131
|
+
"outline-sky-500 outline-offset-2",
|
|
132
|
+
)}
|
|
133
|
+
|
|
134
|
+
$autoGenerateWrapper
|
|
135
|
+
$id={ctxmLabel}
|
|
136
|
+
$inherit={(menu) => ({
|
|
137
|
+
parent: menu.id,
|
|
138
|
+
children: menu.menu
|
|
139
|
+
})}
|
|
140
|
+
$menu={[
|
|
141
|
+
[
|
|
142
|
+
{
|
|
143
|
+
id: 'copy',
|
|
144
|
+
label: "Copy",
|
|
145
|
+
description: ref?.name,
|
|
146
|
+
onClick: handleCopy
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
ref?.menu ?? null
|
|
150
|
+
]}
|
|
151
|
+
>
|
|
152
|
+
{children}
|
|
153
|
+
</ContextMenu>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default ParseValue;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createContext, useContext, useLayoutEffect, useRef, useState } from "react";
|
|
1
|
+
import { createContext, forwardRef, useContext, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
2
2
|
import { LogItem, LogRole, LogType, Terminal } from "../DebuggerTerminal"
|
|
3
3
|
import clsx from "clsx";
|
|
4
4
|
import { KeyboardButton, matchBool, matchKeyEvent, matchMouseEvent, MouseButton, Optional } from "@hrnec06/util";
|
|
5
|
-
import ParseValue from "./DebugParser";
|
|
5
|
+
import ParseValue, { ParseValueRef } from "./DebugParser";
|
|
6
6
|
import { ChevronRight, CircleAlert, SendIcon, TerminalIcon, Trash, TriangleAlert } from "lucide-react";
|
|
7
7
|
import ResizeableBox from "../../ResizeableBox/ResizeableBox";
|
|
8
8
|
import { Chevron_Toggle } from "../DebuggerSymbols";
|
|
@@ -28,10 +28,25 @@ interface DebugTerminalProps {
|
|
|
28
28
|
path: string[],
|
|
29
29
|
defaultExpanded?: boolean
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const DebugTerminal = forwardRef<ParseValueRef, DebugTerminalProps>(({
|
|
32
|
+
terminal,
|
|
33
|
+
path,
|
|
34
|
+
defaultExpanded
|
|
35
|
+
}, ref) => {
|
|
33
36
|
const [showing, setShowing] = useState(!!defaultExpanded);
|
|
34
37
|
|
|
38
|
+
useImperativeHandle(ref, () => ({
|
|
39
|
+
name: `Terminal ${terminal.name}`,
|
|
40
|
+
copyValue: terminal.getLog().reduce((prev, curr) => prev + `${curr.role === LogRole.Input ? '> ' : `${LogType[curr.type]}: `}${JSON.stringify(curr.value)}` + "\n", ""),
|
|
41
|
+
menu: [
|
|
42
|
+
{
|
|
43
|
+
id: "terminal-clear",
|
|
44
|
+
label: "Clear",
|
|
45
|
+
onClick: () => terminal.clear(),
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}), [terminal.getLog()]);
|
|
49
|
+
|
|
35
50
|
const handleToggle = (value: boolean) => {
|
|
36
51
|
setShowing(value);
|
|
37
52
|
}
|
|
@@ -60,8 +75,8 @@ export default function DebugTerminal({ terminal, path, defaultExpanded }: Debug
|
|
|
60
75
|
|
|
61
76
|
<TerminalMain show={showing} />
|
|
62
77
|
</LocalTerminalContext.Provider>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
78
|
+
);
|
|
79
|
+
});
|
|
65
80
|
|
|
66
81
|
interface TerminalMainProps {
|
|
67
82
|
show: boolean
|
|
@@ -338,4 +353,6 @@ function TerminalInput()
|
|
|
338
353
|
</button>
|
|
339
354
|
</div>
|
|
340
355
|
)
|
|
341
|
-
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export default DebugTerminal;
|
|
@@ -1,23 +1,37 @@
|
|
|
1
|
-
import React, { useLayoutEffect, useMemo, useState } from "react";
|
|
1
|
+
import React, { forwardRef, useImperativeHandle, useLayoutEffect, useMemo, useState } from "react";
|
|
2
2
|
import useDebugger from "../DebuggerContext";
|
|
3
3
|
import { Char_Bracket, Char_Comma, Chevron_Toggle } from "../DebuggerSymbols";
|
|
4
4
|
import ParseValueSimple from "./DebugParserSimple";
|
|
5
|
-
import ParseValue from "./DebugParser";
|
|
5
|
+
import ParseValue, { ParseValueRef } from "./DebugParser";
|
|
6
6
|
|
|
7
7
|
interface ValueArrayProps {
|
|
8
8
|
value: unknown[],
|
|
9
9
|
path: string[],
|
|
10
10
|
defaultExpanded?: boolean
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
const ValueArray = forwardRef<ParseValueRef, ValueArrayProps>(({
|
|
13
13
|
value,
|
|
14
14
|
path,
|
|
15
15
|
defaultExpanded = false
|
|
16
|
-
}
|
|
16
|
+
}, ref) => {
|
|
17
17
|
const debug = useDebugger();
|
|
18
18
|
|
|
19
19
|
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
20
20
|
|
|
21
|
+
useImperativeHandle(ref, () => ({
|
|
22
|
+
name: `Array (${value.length})`,
|
|
23
|
+
copyValue: JSON.stringify(value),
|
|
24
|
+
menu: [
|
|
25
|
+
{
|
|
26
|
+
id: "open-toggle",
|
|
27
|
+
label: expanded ? "Collapse" : "Expand",
|
|
28
|
+
onClick: () => {
|
|
29
|
+
setExpanded(!expanded);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}), [value, expanded]);
|
|
34
|
+
|
|
21
35
|
const children = useMemo(() => {
|
|
22
36
|
const children: React.ReactNode[] = [];
|
|
23
37
|
|
|
@@ -106,7 +120,6 @@ export default function ValueArray({
|
|
|
106
120
|
onClick={() => setExpanded(true)}
|
|
107
121
|
>
|
|
108
122
|
<Char_Bracket text="[" />
|
|
109
|
-
{/* <span className="text-zinc-500">...</span> */}
|
|
110
123
|
{collapsedPreview}
|
|
111
124
|
<Char_Bracket text="]" />
|
|
112
125
|
</div>
|
|
@@ -114,7 +127,7 @@ export default function ValueArray({
|
|
|
114
127
|
}
|
|
115
128
|
</>
|
|
116
129
|
);
|
|
117
|
-
}
|
|
130
|
+
});
|
|
118
131
|
|
|
119
132
|
interface CompactArrayItemProps {
|
|
120
133
|
value: unknown,
|
|
@@ -140,4 +153,6 @@ function CompactArrayItem({
|
|
|
140
153
|
{isLast && (<Char_Comma />)}
|
|
141
154
|
</>
|
|
142
155
|
)
|
|
143
|
-
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export default ValueArray;
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
import { forwardRef, useImperativeHandle } from "react"
|
|
1
2
|
import ValueKeyword from "./ValueKeyword"
|
|
3
|
+
import { ParseValueRef } from "./DebugParser";
|
|
2
4
|
|
|
3
5
|
interface ValueBooleanProps {
|
|
4
6
|
value: boolean
|
|
5
7
|
}
|
|
6
|
-
|
|
8
|
+
const ValueBoolean = forwardRef<ParseValueRef, ValueBooleanProps>(({
|
|
7
9
|
value
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
}, ref) => {
|
|
11
|
+
useImperativeHandle(ref, () => ({
|
|
12
|
+
name: "Boolean",
|
|
13
|
+
copyValue: value ? "true" : "false"
|
|
14
|
+
}), [value]);
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<ValueKeyword value={value ? 'true' : 'false'} />
|
|
18
|
+
)
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export default ValueBoolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { forwardRef, useImperativeHandle } from "react";
|
|
2
|
+
import ValueKeyword from "./ValueKeyword";
|
|
3
|
+
import { ParseValueRef } from "./DebugParser";
|
|
4
|
+
|
|
5
|
+
interface ValueConstantProps {
|
|
6
|
+
value: string
|
|
7
|
+
}
|
|
8
|
+
const ValueConstant = forwardRef<ParseValueRef, ValueConstantProps>(({
|
|
9
|
+
value
|
|
10
|
+
}, ref) => {
|
|
11
|
+
useImperativeHandle(ref, () => ({
|
|
12
|
+
name: value,
|
|
13
|
+
copyValue: value
|
|
14
|
+
}), [value]);
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<ValueKeyword value={value} />
|
|
18
|
+
)
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export default ValueConstant;
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
import { forwardRef, useImperativeHandle } from "react";
|
|
2
|
+
import { ParseValueRef } from "./DebugParser";
|
|
3
|
+
|
|
1
4
|
interface ValueFunctionProps {
|
|
2
5
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
3
6
|
value: Function
|
|
4
7
|
}
|
|
5
|
-
|
|
8
|
+
const ValueFunction = forwardRef<ParseValueRef, ValueFunctionProps>(({
|
|
6
9
|
value
|
|
7
|
-
}
|
|
10
|
+
}, ref) => {
|
|
11
|
+
useImperativeHandle(ref, () => ({
|
|
12
|
+
name: "Function",
|
|
13
|
+
copyValue: value.toString()
|
|
14
|
+
}), [value]);
|
|
15
|
+
|
|
8
16
|
const argsMatch = value.toString().match(/^[^(]*\(\s*([^)]*)\)/);
|
|
9
17
|
let args: string = '';
|
|
10
18
|
|
|
@@ -12,14 +20,18 @@ export default function ValueFunction({
|
|
|
12
20
|
args = argsMatch[1]!;
|
|
13
21
|
}
|
|
14
22
|
|
|
23
|
+
const displayValue = `${value.name}(${args})`;
|
|
24
|
+
|
|
15
25
|
return (
|
|
16
26
|
<>
|
|
17
27
|
<span className="text-[#f2824a]">
|
|
18
28
|
{'ƒ '}
|
|
19
29
|
</span>
|
|
20
30
|
<span className="text-white">
|
|
21
|
-
{
|
|
31
|
+
{displayValue}
|
|
22
32
|
</span>
|
|
23
33
|
</>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export default ValueFunction;
|
|
@@ -1,12 +1,22 @@
|
|
|
1
|
+
import { forwardRef, useImperativeHandle } from "react"
|
|
2
|
+
import { ParseValueRef } from "./DebugParser";
|
|
3
|
+
|
|
1
4
|
interface ValueNumberProps {
|
|
2
5
|
value: number | bigint | string
|
|
3
6
|
}
|
|
4
|
-
|
|
7
|
+
const ValueNumber = forwardRef<ParseValueRef, ValueNumberProps>(({
|
|
5
8
|
value
|
|
6
|
-
}
|
|
9
|
+
}, ref) => {
|
|
10
|
+
useImperativeHandle(ref, () => ({
|
|
11
|
+
name: "Number",
|
|
12
|
+
copyValue: value.toString()
|
|
13
|
+
}), [value]);
|
|
14
|
+
|
|
7
15
|
return (
|
|
8
16
|
<span className="text-[#a7ce9b]">
|
|
9
17
|
{value}
|
|
10
18
|
</span>
|
|
11
|
-
)
|
|
12
|
-
}
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export default ValueNumber;
|
|
@@ -1,25 +1,41 @@
|
|
|
1
|
-
import React, { useLayoutEffect, useMemo, useState } from "react";
|
|
1
|
+
import React, { forwardRef, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import useDebugger from "../DebuggerContext";
|
|
3
|
-
import { entries, keys } from "@hrnec06/util";
|
|
3
|
+
import { entries, keys, Nullable } from "@hrnec06/util";
|
|
4
4
|
import { Char_Bracket, Char_Colon, Char_Comma, Chevron_Toggle } from "../DebuggerSymbols";
|
|
5
|
-
import ParseValue from "./DebugParser";
|
|
5
|
+
import ParseValue, { DebugCTXMProvider, ParseValueRef } from "./DebugParser";
|
|
6
|
+
import useSyncRef from "../../../hooks/useSyncRef";
|
|
7
|
+
import { useCTXMListener } from "../../ContextMenu/ContextMenuCtx";
|
|
6
8
|
|
|
7
9
|
interface ValueObjectProps {
|
|
8
10
|
value: object,
|
|
9
11
|
path: string[],
|
|
10
12
|
defaultExpanded?: boolean
|
|
11
13
|
}
|
|
12
|
-
|
|
14
|
+
const ValueObject = forwardRef<ParseValueRef, ValueObjectProps>(({
|
|
13
15
|
value,
|
|
14
16
|
path,
|
|
15
17
|
defaultExpanded = false
|
|
16
|
-
}
|
|
18
|
+
}, ref) => {
|
|
17
19
|
const debug = useDebugger();
|
|
18
20
|
|
|
19
21
|
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
20
22
|
|
|
21
23
|
const objectEntries = entries(value);
|
|
22
24
|
|
|
25
|
+
useImperativeHandle(ref, () => ({
|
|
26
|
+
name: `Object {${objectEntries.length}}`,
|
|
27
|
+
copyValue: JSON.stringify(value),
|
|
28
|
+
menu: [
|
|
29
|
+
{
|
|
30
|
+
id: "open-toggle",
|
|
31
|
+
label: expanded ? "Collapse" : "Expand",
|
|
32
|
+
onClick: () => {
|
|
33
|
+
setExpanded(!expanded);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}), [value, expanded]);
|
|
38
|
+
|
|
23
39
|
const collapsedPreview = useMemo(() => {
|
|
24
40
|
const children: React.ReactNode[] = [];
|
|
25
41
|
const keyList = keys(value);
|
|
@@ -48,6 +64,8 @@ export default function ValueObject({
|
|
|
48
64
|
setExpanded(state);
|
|
49
65
|
}
|
|
50
66
|
|
|
67
|
+
const ctxmListener = useCTXMListener();
|
|
68
|
+
|
|
51
69
|
return (
|
|
52
70
|
<>
|
|
53
71
|
<Chevron_Toggle expanded={expanded} onToggle={handleExpand} />
|
|
@@ -65,16 +83,13 @@ export default function ValueObject({
|
|
|
65
83
|
{
|
|
66
84
|
objectEntries.map(([key, value], ix) => {
|
|
67
85
|
return (
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
<Char_Comma />
|
|
76
|
-
)}
|
|
77
|
-
</li>
|
|
86
|
+
<ValueObjectEntry
|
|
87
|
+
key={ix}
|
|
88
|
+
entryKey={key}
|
|
89
|
+
value={value}
|
|
90
|
+
path={path}
|
|
91
|
+
isLast={ix === objectEntries.length - 1}
|
|
92
|
+
/>
|
|
78
93
|
);
|
|
79
94
|
})
|
|
80
95
|
}
|
|
@@ -97,7 +112,7 @@ export default function ValueObject({
|
|
|
97
112
|
}
|
|
98
113
|
</>
|
|
99
114
|
);
|
|
100
|
-
}
|
|
115
|
+
});
|
|
101
116
|
|
|
102
117
|
interface ValueObjectKeyProps {
|
|
103
118
|
text: string,
|
|
@@ -110,4 +125,33 @@ function ValueObjectKey({
|
|
|
110
125
|
{text}
|
|
111
126
|
</span>
|
|
112
127
|
)
|
|
113
|
-
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface ValueObjectEntryProps {
|
|
131
|
+
entryKey: string,
|
|
132
|
+
value: unknown,
|
|
133
|
+
path: string[],
|
|
134
|
+
isLast: boolean
|
|
135
|
+
}
|
|
136
|
+
function ValueObjectEntry({
|
|
137
|
+
entryKey: key,
|
|
138
|
+
value,
|
|
139
|
+
path,
|
|
140
|
+
isLast
|
|
141
|
+
}: ValueObjectEntryProps)
|
|
142
|
+
{
|
|
143
|
+
return (
|
|
144
|
+
<li>
|
|
145
|
+
<ParseValue value={value} path={[...path, `${key}`]}>
|
|
146
|
+
<ValueObjectKey text={key} />
|
|
147
|
+
<Char_Colon />
|
|
148
|
+
</ParseValue>
|
|
149
|
+
|
|
150
|
+
{!isLast && (
|
|
151
|
+
<Char_Comma />
|
|
152
|
+
)}
|
|
153
|
+
</li>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default ValueObject;
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import { padString } from "@hrnec06/util"
|
|
2
|
+
import { forwardRef, useImperativeHandle } from "react"
|
|
3
|
+
import { ParseValueRef } from "./DebugParser";
|
|
2
4
|
|
|
3
5
|
interface ValueStringProps {
|
|
4
6
|
value: string,
|
|
5
7
|
noEncase?: boolean
|
|
6
8
|
}
|
|
7
|
-
|
|
9
|
+
const ValueString = forwardRef<ParseValueRef, ValueStringProps>(({
|
|
8
10
|
value,
|
|
9
11
|
noEncase = false
|
|
10
|
-
}
|
|
12
|
+
}, ref) => {
|
|
13
|
+
useImperativeHandle(ref, () => ({
|
|
14
|
+
name: "String",
|
|
15
|
+
copyValue: value
|
|
16
|
+
}), [value]);
|
|
17
|
+
|
|
11
18
|
return (
|
|
12
19
|
<span className="text-[#ce9178]">
|
|
13
20
|
{padString(value, noEncase ? 0 : 1, '"')}
|
|
14
21
|
</span>
|
|
15
|
-
)
|
|
16
|
-
}
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export default ValueString;
|
|
@@ -3,7 +3,7 @@ import clsx from "clsx";
|
|
|
3
3
|
import { useEffect } from "react";
|
|
4
4
|
import DragAreas from "./DragAreas";
|
|
5
5
|
import useSignal from "../../hooks/useSignal";
|
|
6
|
-
import useUpdatedRef from "../../hooks/
|
|
6
|
+
import useUpdatedRef from "../../hooks/useLatestRef";
|
|
7
7
|
import useListener from "../../hooks/useListener";
|
|
8
8
|
import { ResizeableBoxContext } from "./ResizeableBoxContext";
|
|
9
9
|
import RB from "./ResizeableBox.types";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export default function useDebounce<T>(value: T, delay: number, updateOnUnmount: boolean = false): T
|
|
4
|
+
{
|
|
5
|
+
const [debounced, setValue] = useState(value);
|
|
6
|
+
|
|
7
|
+
const update = useCallback((value: T) => setValue(value), []);
|
|
8
|
+
|
|
9
|
+
useLayoutEffect(() => {
|
|
10
|
+
let updated = false;
|
|
11
|
+
|
|
12
|
+
const t = setTimeout(() => {
|
|
13
|
+
update(value);
|
|
14
|
+
updated = true;
|
|
15
|
+
}, delay);
|
|
16
|
+
|
|
17
|
+
return () => {
|
|
18
|
+
clearTimeout(t);
|
|
19
|
+
|
|
20
|
+
if (!updated && updateOnUnmount)
|
|
21
|
+
update(value);
|
|
22
|
+
}
|
|
23
|
+
}, [value]);
|
|
24
|
+
|
|
25
|
+
return debounced;
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import useLatestRef from "./useLatestRef";
|
|
3
|
+
|
|
4
|
+
export default function useEvent<
|
|
5
|
+
F extends (...args: any[]) => any,
|
|
6
|
+
P extends any[] = Parameters<F>,
|
|
7
|
+
R = ReturnType<F>,
|
|
8
|
+
> (
|
|
9
|
+
cb: (...args: P) => R
|
|
10
|
+
)
|
|
11
|
+
{
|
|
12
|
+
const cache = useLatestRef(cb);
|
|
13
|
+
|
|
14
|
+
return useCallback((...args: P) => cache.current(...args), [cache]);
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef } from "react";
|
|
2
|
+
import useUpdateEffect from "./useUpdateEffect";
|
|
3
|
+
|
|
4
|
+
export default function useLatestRef<V>(value: V): React.RefObject<V> {
|
|
5
|
+
const ref = useRef(value);
|
|
6
|
+
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
ref.current = value;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
return ref;
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Nullable } from '@hrnec06/util'
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
|
|
3
|
+
import useEvent from './useEvent';
|
|
4
|
+
|
|
5
|
+
export default function useSyncRef<T>(
|
|
6
|
+
ref: React.RefObject<T>,
|
|
7
|
+
state: (value: T) => void
|
|
8
|
+
)
|
|
9
|
+
{
|
|
10
|
+
const syncRefs = useEvent((value: T) => {
|
|
11
|
+
ref.current = value;
|
|
12
|
+
|
|
13
|
+
state(value);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return syncRefs;
|
|
17
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { disposables, mapRecord, Nullable } from "@hrnec06/util";
|
|
2
|
-
import { useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { useEffect, useEffectEvent, useLayoutEffect, useRef, useState } from "react";
|
|
3
3
|
import useFlags from "./useFlags";
|
|
4
4
|
import useDisposables from "./useDisposables";
|
|
5
5
|
|
|
@@ -235,6 +235,7 @@ function prepareTransition(
|
|
|
235
235
|
if (inFlight?.current)
|
|
236
236
|
{
|
|
237
237
|
prepare();
|
|
238
|
+
|
|
238
239
|
return;
|
|
239
240
|
}
|
|
240
241
|
|