@hrnec06/react_utils 1.1.0 → 1.2.1

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.
@@ -0,0 +1,45 @@
1
+ import useDebugger from "../DebuggerContext";
2
+ import { matchPath } from "../DebuggerLogic";
3
+ import ValueArray from "./ValueArray";
4
+ import ValueBoolean from "./ValueBoolean";
5
+ import ValueFunction from "./ValueFunction";
6
+ import ValueKeyword from "./ValueKeyword";
7
+ import ValueNumber from "./ValueNumber";
8
+ import ValueObject from "./ValueObject";
9
+ import ValueString from "./ValueString";
10
+ import ValueSymbol from "./ValueSymbol";
11
+
12
+ interface ParseValueProps {
13
+ value: unknown,
14
+ path?: string[]
15
+ }
16
+ export default function ParseValue({
17
+ value,
18
+ path = []
19
+ }: ParseValueProps) {
20
+ const debug = useDebugger();
21
+
22
+ switch (typeof value) {
23
+ case 'string': return (<ValueString value={value} />);
24
+ case 'bigint':
25
+ case 'number': return (<ValueNumber value={value} />);
26
+ case 'boolean': return (<ValueBoolean value={value} />);
27
+ case 'undefined': return (<ValueKeyword value={"undefined"} />);
28
+ case 'function': return (<ValueFunction value={value} />);
29
+ case 'symbol': return (<ValueSymbol value={value} />)
30
+ case 'object': {
31
+ if (value === null)
32
+ return (<ValueKeyword value={"null"} />)
33
+
34
+ const isRootObject = path.length === 0;
35
+
36
+ const shouldBeExpanded = (isRootObject && debug.options.openRoot) || (matchPath(path, debug.paths.open) && !matchPath(path, debug.paths.exclude));
37
+
38
+ if (Array.isArray(value)) {
39
+ return <ValueArray value={value} path={path} defaultExpanded={shouldBeExpanded} />
40
+ }
41
+
42
+ return <ValueObject value={value} path={path} defaultExpanded={shouldBeExpanded} />
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,53 @@
1
+ import ValueBoolean from "./ValueBoolean";
2
+ import ValueFunction from "./ValueFunction";
3
+ import ValueKeyword from "./ValueKeyword";
4
+ import ValueNumber from "./ValueNumber";
5
+ import ValueString from "./ValueString";
6
+ import ValueSymbol from "./ValueSymbol";
7
+
8
+ interface ParseValueSimpleProps {
9
+ value: unknown
10
+ }
11
+ export default function ParseValueSimple({
12
+ value
13
+ }: ParseValueSimpleProps): React.ReactNode {
14
+ const MAX_LENGTH = 16;
15
+
16
+ switch (typeof value) {
17
+ case 'bigint':
18
+ case 'number': {
19
+ const numStr = value.toString();
20
+
21
+ if (numStr.length > MAX_LENGTH)
22
+ return <ValueKeyword value={typeof value} />;
23
+
24
+ return <ValueNumber value={value} />
25
+ }
26
+ case 'boolean': return <ValueBoolean value={value} />
27
+ case 'function': return <ValueFunction value={value} />
28
+ case 'object': {
29
+ if (value === null)
30
+ return <ValueKeyword value="null" />
31
+
32
+ const className = Object.getPrototypeOf(value) !== Object.prototype ? value.constructor.name : null;
33
+
34
+ if (className === null || className.length > MAX_LENGTH)
35
+ return <ValueKeyword value="object" />
36
+
37
+ return className;
38
+ }
39
+ case 'string': {
40
+ if (value.length > MAX_LENGTH)
41
+ return <ValueString value="string" noEncase />
42
+
43
+ return <ValueString value={value} />;
44
+ }
45
+ case 'symbol': {
46
+ if (!value.description || value.description.length > MAX_LENGTH)
47
+ return <ValueKeyword value="Symbol" />
48
+
49
+ return <ValueSymbol value={value} />
50
+ }
51
+ case 'undefined': return <ValueKeyword value="undefined" />
52
+ }
53
+ }
@@ -0,0 +1,143 @@
1
+ import React, { useLayoutEffect, useMemo, useState } from "react";
2
+ import useDebugger from "../DebuggerContext";
3
+ import { Char_Bracket, Char_Comma, Chevron_Toggle } from "../DebuggerSymbols";
4
+ import ParseValueSimple from "./DebugParserSimple";
5
+ import ParseValue from "./DebugParser";
6
+
7
+ interface ValueArrayProps {
8
+ value: unknown[],
9
+ path: string[],
10
+ defaultExpanded?: boolean
11
+ }
12
+ export default function ValueArray({
13
+ value,
14
+ path,
15
+ defaultExpanded = false
16
+ }: ValueArrayProps) {
17
+ const debug = useDebugger();
18
+
19
+ const [expanded, setExpanded] = useState(defaultExpanded);
20
+
21
+ const children = useMemo(() => {
22
+ const children: React.ReactNode[] = [];
23
+
24
+ let ix = 0;
25
+
26
+ while (ix < value.length) {
27
+ const nextTarget = Math.min(ix + debug.options.compactArrays, value.length);
28
+
29
+ children.push(
30
+ <li key={ix}>
31
+ {
32
+ (() => {
33
+ const children: React.ReactNode[] = [];
34
+
35
+ for (;ix < nextTarget; ix++) {
36
+ children.push(
37
+ <CompactArrayItem
38
+ key={ix}
39
+ value={value[ix]}
40
+ path={[...path, `${ix}`]}
41
+ isLast={ix < value.length - 1}
42
+ />
43
+ );
44
+ }
45
+
46
+ return children;
47
+ })()
48
+ }
49
+ </li>
50
+ );
51
+
52
+ ix = nextTarget;
53
+ }
54
+
55
+ return children;
56
+ }, [debug.options.compactArrays, value]);
57
+
58
+ const collapsedPreview = useMemo(() => {
59
+ const children: React.ReactNode[] = [];
60
+
61
+ for (let i = 0; i < Math.min(value.length, 6); i++) {
62
+ children.push(
63
+ <CompactArrayItem
64
+ key={i}
65
+ value={value[i]}
66
+ path={path}
67
+ isLast={i < value.length - 1}
68
+ simple
69
+ />
70
+ );
71
+ }
72
+
73
+ if (value.length > 6)
74
+ children.push(<span key={'rest'} className="text-zinc-500">...</span>);
75
+
76
+ return children;
77
+ }, [value]);
78
+
79
+ useLayoutEffect(() => {
80
+ debug.event.expand(path, expanded);
81
+ }, [expanded]);
82
+
83
+ const handleExpand = (state: boolean) => {
84
+ setExpanded(state);
85
+ }
86
+
87
+ return (
88
+ <>
89
+ <Chevron_Toggle expanded={expanded} onToggle={handleExpand} />
90
+
91
+ <span className="text-white" >{`(${value.length}) `}</span>
92
+ {
93
+ expanded ? (
94
+ <>
95
+ <Char_Bracket text="[" />
96
+
97
+ <ul className="pl-4 ml-1 border-l border-l-[#4b4b4b]">
98
+ {children}
99
+ </ul>
100
+
101
+ <Char_Bracket text="]" />
102
+ </>
103
+ ) : (
104
+ <div
105
+ className="inline-block cursor-pointer"
106
+ onClick={() => setExpanded(true)}
107
+ >
108
+ <Char_Bracket text="[" />
109
+ {/* <span className="text-zinc-500">...</span> */}
110
+ {collapsedPreview}
111
+ <Char_Bracket text="]" />
112
+ </div>
113
+ )
114
+ }
115
+ </>
116
+ );
117
+ }
118
+
119
+ interface CompactArrayItemProps {
120
+ value: unknown,
121
+ isLast: boolean,
122
+ path: string[],
123
+ simple?: boolean
124
+ }
125
+ function CompactArrayItem({
126
+ value,
127
+ isLast,
128
+ path,
129
+ simple
130
+ }: CompactArrayItemProps)
131
+ {
132
+ return (
133
+ <>
134
+ {
135
+ !simple
136
+ ? <ParseValue value={value} path={path} />
137
+ : <ParseValueSimple value={value} />
138
+ }
139
+
140
+ {isLast && (<Char_Comma />)}
141
+ </>
142
+ )
143
+ }
@@ -0,0 +1,10 @@
1
+ import ValueKeyword from "./ValueKeyword"
2
+
3
+ interface ValueBooleanProps {
4
+ value: boolean
5
+ }
6
+ export default function ValueBoolean({
7
+ value
8
+ }: ValueBooleanProps) {
9
+ return <ValueKeyword value={value ? 'true' : 'false'} />
10
+ }
@@ -0,0 +1,25 @@
1
+ interface ValueFunctionProps {
2
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
3
+ value: Function
4
+ }
5
+ export default function ValueFunction({
6
+ value
7
+ }: ValueFunctionProps) {
8
+ const argsMatch = value.toString().match(/^[^(]*\(\s*([^)]*)\)/);
9
+ let args: string = '';
10
+
11
+ if (argsMatch && argsMatch.length > 1) {
12
+ args = argsMatch[1]!;
13
+ }
14
+
15
+ return (
16
+ <>
17
+ <span className="text-[#f2824a]">
18
+ {'ƒ '}
19
+ </span>
20
+ <span>
21
+ {`${value.name}(${args})`}
22
+ </span>
23
+ </>
24
+ )
25
+ }
@@ -0,0 +1,12 @@
1
+ interface ValueKeywordProps {
2
+ value: string
3
+ }
4
+ export default function ValueKeyword({
5
+ value
6
+ }: ValueKeywordProps) {
7
+ return (
8
+ <span className="text-[#439ccb]">
9
+ {value}
10
+ </span>
11
+ )
12
+ }
@@ -0,0 +1,12 @@
1
+ interface ValueNumberProps {
2
+ value: number | bigint
3
+ }
4
+ export default function ValueNumber({
5
+ value
6
+ }: ValueNumberProps) {
7
+ return (
8
+ <span className="text-[#a7ce9b]">
9
+ {value}
10
+ </span>
11
+ )
12
+ }
@@ -0,0 +1,113 @@
1
+ import React, { useLayoutEffect, useMemo, useState } from "react";
2
+ import useDebugger from "../DebuggerContext";
3
+ import { entries, keys } from "@hrnec06/util";
4
+ import { Char_Bracket, Char_Colon, Char_Comma, Chevron_Toggle } from "../DebuggerSymbols";
5
+ import ParseValue from "./DebugParser";
6
+
7
+ interface ValueObjectProps {
8
+ value: object,
9
+ path: string[],
10
+ defaultExpanded?: boolean
11
+ }
12
+ export default function ValueObject({
13
+ value,
14
+ path,
15
+ defaultExpanded = false
16
+ }: ValueObjectProps) {
17
+ const debug = useDebugger();
18
+
19
+ const [expanded, setExpanded] = useState(defaultExpanded);
20
+
21
+ const objectEntries = entries(value);
22
+
23
+ const collapsedPreview = useMemo(() => {
24
+ const children: React.ReactNode[] = [];
25
+ const keyList = keys(value);
26
+
27
+ for (let i = 0; i < Math.min(keyList.length, 6); i++) {
28
+ children.push(
29
+ <React.Fragment key={i}>
30
+ <ValueObjectKey text={keyList[i]!} />
31
+ {i < keyList.length - 1 && <Char_Comma />}
32
+ </React.Fragment>
33
+ );
34
+ }
35
+
36
+ if (keyList.length > 6)
37
+ children.push(<span key={'rest'} className="text-zinc-500">...</span>);
38
+
39
+ return children;
40
+ }, [value]);
41
+
42
+ useLayoutEffect(() => {
43
+ debug.event.expand(path, expanded);
44
+ }, [expanded]);
45
+
46
+
47
+ const handleExpand = (state: boolean) => {
48
+ setExpanded(state);
49
+ }
50
+
51
+ return (
52
+ <>
53
+ <Chevron_Toggle expanded={expanded} onToggle={handleExpand} />
54
+
55
+ {
56
+ Object.getPrototypeOf(value) !== Object.prototype && <span>({value.constructor.name}) </span>
57
+ }
58
+
59
+ {
60
+ expanded ? (
61
+ <>
62
+ <Char_Bracket text="{" />
63
+
64
+ <ul className="pl-4 ml-1 border-l border-l-[#4b4b4b]">
65
+ {
66
+ objectEntries.map(([key, value], ix) => {
67
+ return (
68
+ <li key={ix}>
69
+ <ValueObjectKey text={key} />
70
+ <Char_Colon />
71
+
72
+ <ParseValue value={value} path={[...path, `${key}`]} />
73
+
74
+ {ix < objectEntries.length - 1 && (
75
+ <Char_Comma />
76
+ )}
77
+ </li>
78
+ );
79
+ })
80
+ }
81
+ </ul>
82
+
83
+ <Char_Bracket text="}" />
84
+ </>
85
+ ) : (
86
+ <div
87
+ className="inline-block cursor-pointer"
88
+ onClick={() => setExpanded(true)}
89
+ >
90
+ <Char_Bracket text="{" />
91
+ {' '}
92
+ {collapsedPreview}
93
+ {' '}
94
+ <Char_Bracket text="}" />
95
+ </div>
96
+ )
97
+ }
98
+ </>
99
+ );
100
+ }
101
+
102
+ interface ValueObjectKeyProps {
103
+ text: string,
104
+ }
105
+ function ValueObjectKey({
106
+ text
107
+ }: ValueObjectKeyProps) {
108
+ return (
109
+ <span className="text-[#9CDCF0]">
110
+ {text}
111
+ </span>
112
+ )
113
+ }
@@ -0,0 +1,16 @@
1
+ import { padString } from "@hrnec06/util"
2
+
3
+ interface ValueStringProps {
4
+ value: string,
5
+ noEncase?: boolean
6
+ }
7
+ export default function ValueString({
8
+ value,
9
+ noEncase = false
10
+ }: ValueStringProps) {
11
+ return (
12
+ <span className="text-[#ce9178]">
13
+ {padString(value, noEncase ? 0 : 1, '"')}
14
+ </span>
15
+ )
16
+ }
@@ -0,0 +1,20 @@
1
+ import { Char_Bracket } from "../DebuggerSymbols"
2
+ import ValueKeyword from "./ValueKeyword"
3
+
4
+ interface ValueSymbolProps {
5
+ value: symbol
6
+ }
7
+ export default function ValueSymbol({
8
+ value
9
+ }: ValueSymbolProps) {
10
+ return (
11
+ <>
12
+ <ValueKeyword value="Symbol" />
13
+ <Char_Bracket text="(" />
14
+ <span>
15
+ {value.description}
16
+ </span>
17
+ <Char_Bracket text=")" />
18
+ </>
19
+ )
20
+ }
@@ -0,0 +1,47 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ interface KeyListenerOptions {
4
+ ctrl?: boolean,
5
+ shift?: boolean,
6
+ alt?: boolean
7
+ }
8
+ export default function useKeyListener(key: string, options?: KeyListenerOptions) {
9
+ const [pressing, setPressing] = useState(false);
10
+
11
+ const keyMatching = (e: KeyboardEvent, strict: boolean): boolean => {
12
+ if (strict && options?.ctrl && !e.ctrlKey)
13
+ return false;
14
+
15
+ if (strict && options?.shift && !e.shiftKey)
16
+ return false;
17
+
18
+ if (strict && options?.alt && !e.altKey)
19
+ return false;
20
+
21
+ return e.code == key;
22
+ }
23
+
24
+ useEffect(() => {
25
+ const downListener = (e: KeyboardEvent) => {
26
+ if (!keyMatching(e, true)) return;
27
+
28
+ setPressing(true);
29
+ }
30
+
31
+ const upListener = (e: KeyboardEvent) => {
32
+ if (!keyMatching(e, false)) return;
33
+
34
+ setPressing(false);
35
+ }
36
+
37
+ window.addEventListener('keydown', downListener);
38
+ window.addEventListener('keyup', upListener);
39
+
40
+ return () => {
41
+ window.removeEventListener('keydown', downListener);
42
+ window.removeEventListener('keyup', upListener);
43
+ }
44
+ }, []);
45
+
46
+ return pressing;
47
+ }
@@ -0,0 +1,11 @@
1
+ import { DependencyList, useEffect } from "react"
2
+
3
+ export default function useListener<E extends keyof GlobalEventHandlersEventMap>(element: Node, event: E, listener: (event: GlobalEventHandlersEventMap[E]) => void, deps?: DependencyList) {
4
+ useEffect(() => {
5
+ element.addEventListener(event, listener as EventListenerOrEventListenerObject);
6
+
7
+ return () => {
8
+ element.removeEventListener(event, listener as EventListenerOrEventListenerObject);
9
+ }
10
+ }, deps);
11
+ }
@@ -0,0 +1,31 @@
1
+ import { ReactUtils } from "@hrnec06/util";
2
+ import { useState } from "react";
3
+
4
+ export default function useSignal<T>(value: T): Signal<T>
5
+ {
6
+ const [_value, _setValue] = useState(value);
7
+
8
+ const signal = new Signal(_value, _setValue);
9
+
10
+ return signal;
11
+ }
12
+
13
+ export class Signal<T>
14
+ {
15
+ constructor(
16
+ private _value: T,
17
+ private setState: ReactUtils.SetState<T>
18
+ )
19
+ {
20
+ }
21
+
22
+ public set value(value: T)
23
+ {
24
+ this.setState(value);
25
+ }
26
+
27
+ public get value(): T
28
+ {
29
+ return this._value;
30
+ }
31
+ }
@@ -0,0 +1,14 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+
3
+ export default function useUpdateEffect(callback: React.EffectCallback, value: unknown[]) {
4
+ const isFirstRun = useRef(true);
5
+
6
+ useEffect(() => {
7
+ if (isFirstRun.current) {
8
+ isFirstRun.current = false;
9
+ return;
10
+ }
11
+
12
+ return callback();
13
+ }, value);
14
+ }
@@ -0,0 +1,12 @@
1
+ import { useRef } from "react";
2
+ import useUpdateEffect from "./useUpdateEffect";
3
+
4
+ export default function useUpdatedRef<V>(value: V): React.RefObject<V> {
5
+ const ref = useRef(value);
6
+
7
+ useUpdateEffect(() => {
8
+ ref.current = value;
9
+ }, [value]);
10
+
11
+ return ref;
12
+ }
@@ -0,0 +1,22 @@
1
+ import { Vector2 } from "@hrnec06/util";
2
+ import { useEffect, useState } from "react";
3
+
4
+ export default function useWindowSize(): Vector2 {
5
+ const [dimensions, setDimensions] = useState<Vector2>([1920, 1080]);
6
+
7
+ useEffect(() => {
8
+ setDimensions([window.innerWidth, window.innerHeight]);
9
+
10
+ const listener = () => {
11
+ setDimensions([window.innerWidth, window.innerHeight]);
12
+ }
13
+
14
+ window.addEventListener('resize', listener);
15
+
16
+ return () => {
17
+ window.removeEventListener('resize', listener);
18
+ }
19
+ }, []);
20
+
21
+ return dimensions;
22
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,17 @@
1
+ import useKeyListener from "./hooks/useKeyListener";
2
+ import useListener from "./hooks/useListener";
3
+ import useUpdatedRef from "./hooks/useUpdatedRef";
4
+ import useUpdateEffect from "./hooks/useUpdateEffect";
5
+ import useWindowSize from "./hooks/useWindowSize";
6
+ import useSignal from "./hooks/useSignal";
7
+ import Debugger from './debugger/Debugger';
8
+
9
+ export {
10
+ useKeyListener,
11
+ useListener,
12
+ useUpdatedRef,
13
+ useUpdateEffect,
14
+ useWindowSize,
15
+ useSignal,
16
+ Debugger
17
+ };
package/dist/index.d.mts DELETED
@@ -1,40 +0,0 @@
1
- import { DependencyList } from 'react';
2
- import { Vector2, ReactUtils } from '@hrnec06/util';
3
- import * as react_jsx_runtime from 'react/jsx-runtime';
4
-
5
- interface KeyListenerOptions {
6
- ctrl?: boolean;
7
- shift?: boolean;
8
- alt?: boolean;
9
- }
10
- declare function useKeyListener(key: string, options?: KeyListenerOptions): boolean;
11
-
12
- declare function useListener<E extends keyof GlobalEventHandlersEventMap>(element: Node, event: E, listener: (event: GlobalEventHandlersEventMap[E]) => void, deps?: DependencyList): void;
13
-
14
- declare function useUpdatedRef<V>(value: V): React.RefObject<V>;
15
-
16
- declare function useUpdateEffect(callback: React.EffectCallback, value: unknown[]): void;
17
-
18
- declare function useWindowSize(): Vector2;
19
-
20
- declare function useSignal<T>(value: T): Signal<T>;
21
- declare class Signal<T> {
22
- private _value;
23
- private setState;
24
- constructor(_value: T, setState: ReactUtils.SetState<T>);
25
- set value(value: T);
26
- get value(): T;
27
- }
28
-
29
- interface DebugProps {
30
- value: unknown;
31
- openPaths?: string[];
32
- excludePaths?: string[];
33
- size?: 'normal' | 'big' | 'tiny';
34
- compactArrays?: number;
35
- autoHeight?: boolean;
36
- openRoot?: boolean;
37
- }
38
- declare function Debug({ value, openPaths, excludePaths, size, compactArrays, autoHeight, openRoot }: DebugProps): react_jsx_runtime.JSX.Element;
39
-
40
- export { Debug as Debugger, useKeyListener, useListener, useSignal, useUpdateEffect, useUpdatedRef, useWindowSize };