@hrnec06/react_utils 1.4.1 → 1.5.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 +4 -3
- package/src/components/Debugger/Debugger.tsx +5 -2
- package/src/components/Debugger/DebuggerSymbols.tsx +6 -1
- package/src/components/Debugger/DebuggerTerminal.tsx +336 -0
- package/src/components/Debugger/parser/DebugParser.tsx +5 -0
- package/src/components/Debugger/parser/DebugParserSimple.tsx +12 -0
- package/src/components/Debugger/parser/DebugTerminal.tsx +341 -0
- package/src/components/Dialog/Dialog.tsx +50 -0
- package/src/components/Dialog/DialogBackdrop.tsx +45 -0
- package/src/components/Dialog/DialogCloseButton.tsx +25 -0
- package/src/components/Dialog/DialogContext.ts +21 -0
- package/src/components/Dialog/DialogPanel.tsx +35 -0
- package/src/components/ResizeableBox/DragAreas.tsx +26 -23
- package/src/components/ResizeableBox/ResizeableBox.tsx +6 -2
- package/src/hooks/useDisposables.ts +13 -0
- package/src/hooks/useFlags.ts +32 -0
- package/src/hooks/useNamespacedId.ts +19 -0
- package/src/hooks/useTransition.ts +261 -0
- package/src/index.ts +11 -4
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { disposables, mapRecord, Nullable } from "@hrnec06/util";
|
|
2
|
+
import { useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import useFlags from "./useFlags";
|
|
4
|
+
import useDisposables from "./useDisposables";
|
|
5
|
+
|
|
6
|
+
enum TransitionState {
|
|
7
|
+
None = 0,
|
|
8
|
+
|
|
9
|
+
Closed = 1 << 0,
|
|
10
|
+
|
|
11
|
+
Enter = 1 << 1,
|
|
12
|
+
Leave = 1 << 2,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TransitionStates {
|
|
16
|
+
enter?: boolean,
|
|
17
|
+
leave?: boolean,
|
|
18
|
+
closed?: boolean,
|
|
19
|
+
transition?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function transitionDataAttributes(data: TransitionStates)
|
|
23
|
+
{
|
|
24
|
+
return mapRecord(data, (key, value) => [
|
|
25
|
+
`data-${key}`,
|
|
26
|
+
value === true ? "true" : undefined
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default function useTransition(
|
|
31
|
+
enabled: boolean,
|
|
32
|
+
element: Nullable<HTMLElement>,
|
|
33
|
+
show: boolean
|
|
34
|
+
): [visible: boolean, data: TransitionStates]
|
|
35
|
+
{
|
|
36
|
+
const [visible, setVisible] = useState(show);
|
|
37
|
+
|
|
38
|
+
const flags = useFlags(
|
|
39
|
+
(enabled && visible) ? TransitionState.Enter | TransitionState.Closed : TransitionState.None
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const inFlight = useRef(false);
|
|
43
|
+
const cancelledRef = useRef(false);
|
|
44
|
+
|
|
45
|
+
const d = useDisposables();
|
|
46
|
+
|
|
47
|
+
useLayoutEffect(() => {
|
|
48
|
+
if (!enabled) return;
|
|
49
|
+
|
|
50
|
+
if (show)
|
|
51
|
+
{
|
|
52
|
+
setVisible(true);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!element)
|
|
56
|
+
{
|
|
57
|
+
if (show)
|
|
58
|
+
{
|
|
59
|
+
flags.addFlag(TransitionState.Enter | TransitionState.Closed);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return transition(
|
|
66
|
+
element,
|
|
67
|
+
{
|
|
68
|
+
inFlight: inFlight,
|
|
69
|
+
prepare: () => {
|
|
70
|
+
if (cancelledRef.current)
|
|
71
|
+
{
|
|
72
|
+
cancelledRef.current = false;
|
|
73
|
+
}
|
|
74
|
+
else
|
|
75
|
+
{
|
|
76
|
+
cancelledRef.current = inFlight.current;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
inFlight.current = true;
|
|
80
|
+
|
|
81
|
+
if (cancelledRef.current) return;
|
|
82
|
+
|
|
83
|
+
if (show)
|
|
84
|
+
{
|
|
85
|
+
flags.addFlag(TransitionState.Enter | TransitionState.Closed);
|
|
86
|
+
flags.removeFlag(TransitionState.Leave);
|
|
87
|
+
}
|
|
88
|
+
else
|
|
89
|
+
{
|
|
90
|
+
flags.addFlag(TransitionState.Leave);
|
|
91
|
+
flags.removeFlag(TransitionState.Enter);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
run: () => {
|
|
95
|
+
if (cancelledRef.current)
|
|
96
|
+
{
|
|
97
|
+
if (show)
|
|
98
|
+
{
|
|
99
|
+
flags.addFlag(TransitionState.Leave);
|
|
100
|
+
flags.removeFlag(TransitionState.Enter | TransitionState.Closed);
|
|
101
|
+
}
|
|
102
|
+
else
|
|
103
|
+
{
|
|
104
|
+
flags.addFlag(TransitionState.Enter | TransitionState.Closed);
|
|
105
|
+
flags.removeFlag(TransitionState.Leave);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else
|
|
109
|
+
{
|
|
110
|
+
if (show)
|
|
111
|
+
{
|
|
112
|
+
flags.removeFlag(TransitionState.Closed);
|
|
113
|
+
}
|
|
114
|
+
else
|
|
115
|
+
{
|
|
116
|
+
flags.addFlag(TransitionState.Closed);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
done: () => {
|
|
121
|
+
if (cancelledRef.current)
|
|
122
|
+
{
|
|
123
|
+
if (hasPendingTransitions(element))
|
|
124
|
+
{
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
inFlight.current = false;
|
|
130
|
+
|
|
131
|
+
flags.removeFlag(TransitionState.Enter | TransitionState.Leave | TransitionState.Closed);
|
|
132
|
+
|
|
133
|
+
if (!show)
|
|
134
|
+
{
|
|
135
|
+
setVisible(false);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}, [enabled, show, element, d]);
|
|
141
|
+
|
|
142
|
+
if (!enabled)
|
|
143
|
+
{
|
|
144
|
+
return [
|
|
145
|
+
show,
|
|
146
|
+
{
|
|
147
|
+
closed: undefined,
|
|
148
|
+
enter: undefined,
|
|
149
|
+
leave: undefined,
|
|
150
|
+
transition: undefined
|
|
151
|
+
}
|
|
152
|
+
] as const;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return [
|
|
156
|
+
visible,
|
|
157
|
+
{
|
|
158
|
+
closed: flags.hasFlag(TransitionState.Closed),
|
|
159
|
+
enter: flags.hasFlag(TransitionState.Enter),
|
|
160
|
+
leave: flags.hasFlag(TransitionState.Leave),
|
|
161
|
+
transition: flags.hasFlag(TransitionState.Enter) || flags.hasFlag(TransitionState.Leave)
|
|
162
|
+
}
|
|
163
|
+
] as const;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function transition(
|
|
167
|
+
node: HTMLElement,
|
|
168
|
+
{
|
|
169
|
+
prepare,
|
|
170
|
+
run,
|
|
171
|
+
done,
|
|
172
|
+
inFlight
|
|
173
|
+
}: {
|
|
174
|
+
prepare: () => void,
|
|
175
|
+
run: () => void,
|
|
176
|
+
done: () => void,
|
|
177
|
+
inFlight: React.RefObject<boolean>
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
{
|
|
181
|
+
const d = disposables();
|
|
182
|
+
|
|
183
|
+
prepareTransition(node, {
|
|
184
|
+
prepare,
|
|
185
|
+
inFlight
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
d.nextFrame(() => {
|
|
189
|
+
run();
|
|
190
|
+
|
|
191
|
+
d.requestAnimationFrame(() => {
|
|
192
|
+
d.add(waitForTransition(node, done));
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return d.dispose;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function waitForTransition(node: Nullable<HTMLElement>, done: () => void)
|
|
200
|
+
{
|
|
201
|
+
const d = disposables();
|
|
202
|
+
|
|
203
|
+
if (!node) return d.dispose;
|
|
204
|
+
|
|
205
|
+
let cancelled = false;
|
|
206
|
+
d.add(() => {
|
|
207
|
+
cancelled = true;
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const transitions = node.getAnimations?.().filter((animation) => animation instanceof CSSTransition) ?? [];
|
|
211
|
+
|
|
212
|
+
if (transitions.length === 0)
|
|
213
|
+
{
|
|
214
|
+
done();
|
|
215
|
+
|
|
216
|
+
return d.dispose;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
Promise.allSettled(transitions.map((transition) => transition.finished))
|
|
220
|
+
.then(() => {
|
|
221
|
+
if (!cancelled)
|
|
222
|
+
{
|
|
223
|
+
done();
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return d.dispose;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function prepareTransition(
|
|
231
|
+
node: HTMLElement,
|
|
232
|
+
{ inFlight, prepare }: { inFlight?: React.RefObject<boolean>, prepare: () => void }
|
|
233
|
+
)
|
|
234
|
+
{
|
|
235
|
+
if (inFlight?.current)
|
|
236
|
+
{
|
|
237
|
+
prepare();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const previous = node.style.transition;
|
|
242
|
+
|
|
243
|
+
node.style.transition = "none";
|
|
244
|
+
|
|
245
|
+
prepare();
|
|
246
|
+
|
|
247
|
+
// Trigger a reflow, flushing the CSS changes
|
|
248
|
+
node.offsetHeight;
|
|
249
|
+
|
|
250
|
+
// Reset the transition to what it was before
|
|
251
|
+
node.style.transition = previous;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function hasPendingTransitions(node: HTMLElement): boolean
|
|
255
|
+
{
|
|
256
|
+
const animations = node.getAnimations?.() ?? []
|
|
257
|
+
|
|
258
|
+
return animations.some((animation) => {
|
|
259
|
+
return animation instanceof CSSTransition && animation.playState !== 'finished'
|
|
260
|
+
})
|
|
261
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,17 +6,20 @@ import useWindowSize from "./hooks/useWindowSize";
|
|
|
6
6
|
import useEfficientRef from "./hooks/useEfficientRef";
|
|
7
7
|
import useEfficientState from "./hooks/useEfficientState";
|
|
8
8
|
import useUUID from "./hooks/useUUID";
|
|
9
|
+
import useFlags from "./hooks/useFlags";
|
|
9
10
|
|
|
10
11
|
import useSignal, { Signal } from "./hooks/useSignal";
|
|
11
|
-
|
|
12
12
|
import useLazySignal, { LazySignal } from "./hooks/useLazySignal";
|
|
13
13
|
|
|
14
|
-
import Debugger from './components/Debugger/Debugger';
|
|
14
|
+
import * as Debugger from './components/Debugger/Debugger';
|
|
15
|
+
|
|
15
16
|
import ResizeableBox from "./components/ResizeableBox/ResizeableBox";
|
|
17
|
+
import Dialog from "./components/Dialog/Dialog";
|
|
16
18
|
|
|
17
19
|
import * as util from './lib/utils';
|
|
18
20
|
|
|
19
21
|
export {
|
|
22
|
+
// Hooks
|
|
20
23
|
useKeyListener,
|
|
21
24
|
useListener,
|
|
22
25
|
useUpdatedRef,
|
|
@@ -25,15 +28,19 @@ export {
|
|
|
25
28
|
useEfficientRef,
|
|
26
29
|
useEfficientState,
|
|
27
30
|
useUUID,
|
|
31
|
+
useFlags,
|
|
28
32
|
|
|
33
|
+
// Signals
|
|
29
34
|
useSignal,
|
|
30
35
|
Signal,
|
|
31
|
-
|
|
32
36
|
useLazySignal,
|
|
33
37
|
LazySignal,
|
|
34
38
|
|
|
39
|
+
// Components
|
|
35
40
|
Debugger,
|
|
36
41
|
ResizeableBox,
|
|
42
|
+
Dialog,
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
// Utilities
|
|
45
|
+
util,
|
|
39
46
|
};
|