Skip to content

Commit 805daca

Browse files
committed
Update header.tsx
1 parent 6204698 commit 805daca

File tree

1 file changed

+131
-131
lines changed

1 file changed

+131
-131
lines changed
Lines changed: 131 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
import {
2+
untracked,
3+
useComputed,
4+
useSignal,
5+
useSignalEffect,
6+
} from '@preact/signals';
17
import type { Fiber } from 'bippy';
2-
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
8+
import { useRef } from 'preact/hooks';
39
import { Store } from '~core/index';
410
import { signalIsSettingsOpen } from '~web/state';
511
import { cn, getExtendedDisplayName } from '~web/utils/helpers';
12+
import { constant } from '~web/utils/preact/constant';
613
import { Icon } from '../icon';
714
import { timelineState } from '../inspector/states';
815
import { getOverrideMethods } from '../inspector/utils';
@@ -18,17 +25,16 @@ export const BtnReplay = () => {
1825
// },
1926
// });
2027

21-
const [canEdit, setCanEdit] = useState(false);
22-
const isSettingsOpen = signalIsSettingsOpen.value;
28+
const canEdit = useSignal(false);
2329

24-
useEffect(() => {
30+
useSignalEffect(() => {
2531
const { overrideProps } = getOverrideMethods();
26-
const canEdit = !!overrideProps;
32+
const currentCanEdit = !!overrideProps;
2733

2834
requestAnimationFrame(() => {
29-
setCanEdit(canEdit);
35+
canEdit.value = currentCanEdit;
3036
});
31-
}, []);
37+
});
3238

3339
// const handleReplay = (e: MouseEvent) => {
3440
// e.stopPropagation();
@@ -63,20 +69,24 @@ export const BtnReplay = () => {
6369
// });
6470
// };
6571

66-
if (!canEdit) return null;
67-
68-
return (
69-
<button
70-
type="button"
71-
title="Replay component"
72-
// onClick={handleReplay}
73-
className={cn('react-scan-replay-button', {
74-
'opacity-0 pointer-events-none': isSettingsOpen,
75-
})}
76-
>
77-
<Icon name="icon-replay" />
78-
</button>
79-
);
72+
return useComputed(() => {
73+
if (canEdit.value) {
74+
return (
75+
<button
76+
type="button"
77+
title="Replay component"
78+
// onClick={handleReplay}
79+
// TODO(Alexis): can be granular
80+
className={cn('react-scan-replay-button', {
81+
'opacity-0 pointer-events-none': signalIsSettingsOpen.value,
82+
})}
83+
>
84+
<Icon name="icon-replay" />
85+
</button>
86+
);
87+
}
88+
return null;
89+
});
8090
};
8191
// const useSubscribeFocusedFiber = (onUpdate: () => void) => {
8292
// // biome-ignore lint/correctness/useExhaustiveDependencies: no deps
@@ -100,121 +110,114 @@ export const BtnReplay = () => {
100110
const HeaderInspect = () => {
101111
const refReRenders = useRef<HTMLSpanElement>(null);
102112
const refTiming = useRef<HTMLSpanElement>(null);
103-
const isSettingsOpen = signalIsSettingsOpen.value;
104-
const [currentFiber, setCurrentFiber] = useState<Fiber | null>(null);
113+
const currentFiber = useSignal<Fiber | null>(null);
105114

106-
useEffect(() => {
107-
const unSubState = Store.inspectState.subscribe((state) => {
108-
if (state.kind !== 'focused') return;
115+
// TODO(Alexis): can be computed?
116+
useSignalEffect(() => {
117+
const state = Store.inspectState.value;
109118

110-
const fiber = state.fiber;
111-
if (!fiber) return;
119+
if (state.kind !== 'focused') {
120+
return;
121+
}
112122

113-
setCurrentFiber(fiber);
114-
});
123+
currentFiber.value = state.fiber;
124+
});
115125

116-
return unSubState;
117-
}, []);
118-
119-
useEffect(() => {
120-
const unSubTimeline = timelineState.subscribe((state) => {
121-
if (Store.inspectState.value.kind !== 'focused') return;
122-
if (!refReRenders.current || !refTiming.current) return;
123-
124-
const { totalUpdates, currentIndex, updates, isVisible, windowOffset } =
125-
state;
126-
127-
const reRenders = Math.max(0, totalUpdates - 1);
128-
const headerText = isVisible
129-
? `#${windowOffset + currentIndex} Re-render`
130-
: `${reRenders} Re-renders`;
131-
132-
let formattedTime: string | undefined;
133-
if (reRenders > 0 && currentIndex >= 0 && currentIndex < updates.length) {
134-
const time = updates[currentIndex]?.fiberInfo?.selfTime;
135-
formattedTime =
136-
time > 0
137-
? time < 0.1 - Number.EPSILON
138-
? '< 0.1ms'
139-
: `${Number(time.toFixed(1))}ms`
140-
: undefined;
141-
}
142-
143-
refReRenders.current.dataset.text = `${headerText}${reRenders > 0 && formattedTime ? ' •' : ''}`;
144-
if (formattedTime) {
145-
refTiming.current.dataset.text = formattedTime;
146-
}
147-
});
126+
useSignalEffect(() => {
127+
const state = timelineState.value;
148128

149-
return unSubTimeline;
150-
}, []);
129+
if (untracked(() => Store.inspectState.value.kind !== 'focused')) {
130+
return;
131+
}
132+
if (!refReRenders.current || !refTiming.current) return;
133+
134+
const { totalUpdates, currentIndex, updates, isVisible, windowOffset } =
135+
state;
136+
137+
const reRenders = Math.max(0, totalUpdates - 1);
138+
const headerText = isVisible
139+
? `#${windowOffset + currentIndex} Re-render`
140+
: `${reRenders} Re-renders`;
141+
142+
let formattedTime: string | undefined;
143+
if (reRenders > 0 && currentIndex >= 0 && currentIndex < updates.length) {
144+
const time = updates[currentIndex]?.fiberInfo?.selfTime;
145+
formattedTime =
146+
time > 0
147+
? time < 0.1 - Number.EPSILON
148+
? '< 0.1ms'
149+
: `${Number(time.toFixed(1))}ms`
150+
: undefined;
151+
}
151152

152-
const componentName = useMemo(() => {
153-
if (!currentFiber) return null;
154-
const { name, wrappers, wrapperTypes } = getExtendedDisplayName(currentFiber);
153+
// TODO(Alexis): use props instead?
154+
refReRenders.current.dataset.text = `${headerText}${reRenders > 0 && formattedTime ? ' •' : ''}`;
155+
if (formattedTime) {
156+
refTiming.current.dataset.text = formattedTime;
157+
}
158+
});
159+
160+
const componentName = useComputed(() => {
161+
if (!currentFiber.value) {
162+
return null;
163+
}
164+
const { name, wrappers, wrapperTypes } = getExtendedDisplayName(
165+
currentFiber.value,
166+
);
155167

156168
const title = wrappers.length
157169
? `${wrappers.join('(')}(${name})${')'.repeat(wrappers.length)}`
158-
: name ?? '';
170+
: (name ?? '');
159171

160172
const firstWrapperType = wrapperTypes[0];
173+
174+
// TODO(Alexis): can be granular
161175
return (
162-
<span
163-
title={title}
164-
className="flex items-center gap-x-1"
165-
>
176+
<span title={title} className="flex items-center gap-x-1">
166177
{name ?? 'Unknown'}
167178
<span
168179
title={firstWrapperType?.title}
169180
className="flex items-center gap-x-1 text-[10px] text-purple-400"
170181
>
171-
{
172-
!!firstWrapperType && (
173-
<>
174-
<span
175-
key={firstWrapperType.type}
176-
className={cn(
177-
'rounded py-[1px] px-1',
178-
'truncate',
179-
{
180-
'bg-purple-800 text-neutral-400': firstWrapperType.compiler,
181-
'bg-neutral-700 text-neutral-300': !firstWrapperType.compiler,
182-
'bg-[#5f3f9a] text-white': firstWrapperType.type === 'memo',
183-
}
184-
)}
185-
>
186-
{firstWrapperType.type}
187-
</span>
188-
{firstWrapperType.compiler && (
189-
<span className="text-yellow-300"></span>
190-
)}
191-
</>
192-
)
193-
}
182+
{!!firstWrapperType && (
183+
<>
184+
<span
185+
key={firstWrapperType.type}
186+
className={cn('rounded py-[1px] px-1', 'truncate', {
187+
'bg-purple-800 text-neutral-400': firstWrapperType.compiler,
188+
'bg-neutral-700 text-neutral-300': !firstWrapperType.compiler,
189+
'bg-[#5f3f9a] text-white': firstWrapperType.type === 'memo',
190+
})}
191+
>
192+
{firstWrapperType.type}
193+
</span>
194+
{firstWrapperType.compiler && (
195+
<span className="text-yellow-300"></span>
196+
)}
197+
</>
198+
)}
194199
</span>
195-
{
196-
wrapperTypes.length > 1 && (
197-
<span className="text-[10px] text-neutral-400">
198-
×{wrapperTypes.length - 1}
199-
</span>
200-
)
201-
}
202-
<samp className="text-neutral-500">
203-
{' • '}
204-
</samp>
200+
{wrapperTypes.length > 1 && (
201+
<span className="text-[10px] text-neutral-400">
202+
×{wrapperTypes.length - 1}
203+
</span>
204+
)}
205+
<samp className="text-neutral-500">{' • '}</samp>
205206
</span>
206207
);
207-
}, [currentFiber]);
208+
});
208209

209210
return (
210211
<div
211-
className={cn(
212-
'absolute inset-0 flex items-center gap-x-2',
213-
'translate-y-0',
214-
'transition-transform duration-300',
215-
{
216-
'-translate-y-[200%]': isSettingsOpen,
217-
},
212+
className={useComputed(() =>
213+
cn(
214+
'absolute inset-0 flex items-center gap-x-2',
215+
'translate-y-0',
216+
'transition-transform duration-300',
217+
{
218+
'-translate-y-[200%]': signalIsSettingsOpen.value,
219+
},
220+
),
218221
)}
219222
>
220223
{componentName}
@@ -230,25 +233,22 @@ const HeaderInspect = () => {
230233
);
231234
};
232235

233-
const HeaderSettings = () => {
234-
const isSettingsOpen = signalIsSettingsOpen.value;
235-
return (
236-
<span
237-
data-text="Settings"
238-
className={cn(
239-
'absolute inset-0 flex items-center',
240-
'with-data-text',
241-
'-translate-y-[200%]',
242-
'transition-transform duration-300',
243-
{
244-
'translate-y-0': isSettingsOpen,
245-
},
246-
)}
247-
/>
236+
const HeaderSettings = constant(() => {
237+
const className = useComputed(() =>
238+
cn(
239+
'absolute inset-0 flex items-center',
240+
'with-data-text',
241+
'-translate-y-[200%]',
242+
'transition-transform duration-300',
243+
{
244+
'translate-y-0': signalIsSettingsOpen.value,
245+
},
246+
),
248247
);
249-
};
248+
return <span data-text="Settings" className={className} />;
249+
});
250250

251-
export const Header = () => {
251+
export const Header = constant(() => {
252252
const handleClose = () => {
253253
if (signalIsSettingsOpen.value) {
254254
signalIsSettingsOpen.value = false;
@@ -279,4 +279,4 @@ export const Header = () => {
279279
</button>
280280
</div>
281281
);
282-
};
282+
});

0 commit comments

Comments
 (0)