Skip to content

Commit 366b78b

Browse files
kamilkisielan1ru4l
authored andcommitted
perf
1 parent f0aa7ee commit 366b78b

File tree

3 files changed

+2170
-1278
lines changed

3 files changed

+2170
-1278
lines changed

packages/web/app/src/pages/target-insights-new-filter.tsx

Lines changed: 166 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
forwardRef,
55
Fragment,
66
InputHTMLAttributes,
7+
memo,
78
ReactNode,
89
useCallback,
910
useMemo,
@@ -13,7 +14,6 @@ import { addDays, formatDate } from 'date-fns';
1314
import {
1415
CalendarIcon,
1516
CheckIcon,
16-
ChevronRight,
1717
ChevronRightIcon,
1818
CircleXIcon,
1919
MinusIcon,
@@ -88,22 +88,27 @@ export function FilterTitle(props: { children: ReactNode; changes?: number; onRe
8888
className="group/label text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground w-full text-sm"
8989
>
9090
<CollapsibleTrigger>
91-
<ChevronRightIcon className="mr-2 transition-transform group-data-[state=open]/collapsible:rotate-90" />
92-
{props.children}
93-
{props.changes ? (
94-
<Button
95-
variant="secondary"
96-
size="sm"
97-
className="hover:bg-secondary group ml-auto h-6 w-8 px-1 py-0 text-xs text-gray-500"
98-
onClick={e => {
99-
e.preventDefault();
100-
props.onReset();
101-
}}
102-
>
103-
<CircleXIcon className="hidden size-3 group-hover:block" />
104-
<span className="block group-hover:hidden">{props.changes}</span>
105-
</Button>
106-
) : null}
91+
<>
92+
<ChevronRightIcon className="mr-2 transition-transform group-data-[state=open]/collapsible:rotate-90" />
93+
{props.children}
94+
{props.changes ? (
95+
<Button
96+
variant="secondary"
97+
size="sm"
98+
className="hover:bg-secondary group ml-auto h-6 w-8 px-1 py-0 text-xs text-gray-500"
99+
onClick={e => {
100+
e.preventDefault();
101+
props.onReset();
102+
}}
103+
asChild
104+
>
105+
<>
106+
<CircleXIcon className="hidden size-3 group-hover:block" />
107+
<span className="block group-hover:hidden">{props.changes}</span>
108+
</>
109+
</Button>
110+
) : null}
111+
</>
107112
</CollapsibleTrigger>
108113
</SidebarGroupLabel>
109114
);
@@ -119,89 +124,91 @@ export function FilterContent(props: { children: ReactNode }) {
119124
);
120125
}
121126

122-
export function MultiInputFilter(props: {
123-
name: string;
124-
/**
125-
* Filter's key for the backend and url state
126-
*/
127-
key: string;
128-
selectedValues: string[];
129-
onChange(selectedValues: string[]): void;
130-
}) {
131-
const [traceId, setTraceId] = useState('');
132-
const handleTraceIdChange = useCallback(
133-
(e: React.ChangeEvent<HTMLInputElement>) => {
134-
setTraceId(e.target.value);
135-
},
136-
[setTraceId],
137-
);
127+
export const MultiInputFilter = memo(
128+
(props: {
129+
name: string;
130+
/**
131+
* Filter's key for the backend and url state
132+
*/
133+
key: string;
134+
selectedValues: string[];
135+
onChange(selectedValues: string[]): void;
136+
}) => {
137+
const [traceId, setTraceId] = useState('');
138+
const handleTraceIdChange = useCallback(
139+
(e: React.ChangeEvent<HTMLInputElement>) => {
140+
setTraceId(e.target.value);
141+
},
142+
[setTraceId],
143+
);
138144

139-
const addTraceId = useCallback(() => {
140-
if (!traceId) {
141-
return;
142-
}
145+
const addTraceId = useCallback(() => {
146+
if (!traceId) {
147+
return;
148+
}
143149

144-
if (!props.selectedValues.includes(traceId)) {
145-
props.onChange(props.selectedValues.concat(traceId));
146-
}
150+
if (!props.selectedValues.includes(traceId)) {
151+
props.onChange(props.selectedValues.concat(traceId));
152+
}
147153

148-
setTraceId('');
149-
}, [traceId, setTraceId]);
154+
setTraceId('');
155+
}, [traceId, setTraceId]);
150156

151-
return (
152-
<Filter name={props.name}>
153-
<FilterTitle
154-
changes={props.selectedValues.length}
155-
children={props.name}
156-
onReset={() => props.onChange([])}
157-
/>
158-
<FilterContent>
159-
<form
160-
className="mt-4 flex w-full max-w-sm items-center space-x-2"
161-
onSubmit={e => {
162-
e.preventDefault();
163-
addTraceId();
164-
}}
165-
>
166-
<FilterInput
167-
type="text"
168-
placeholder="Trace ID..."
169-
value={traceId}
170-
onChange={handleTraceIdChange}
171-
/>
172-
<Button
173-
variant="secondary"
174-
className="h-9 w-9 p-0"
175-
type="submit"
176-
onClick={() => {
157+
return (
158+
<Filter name={props.name}>
159+
<FilterTitle
160+
changes={props.selectedValues.length}
161+
children={props.name}
162+
onReset={() => props.onChange([])}
163+
/>
164+
<FilterContent>
165+
<form
166+
className="mt-4 flex w-full max-w-sm items-center space-x-2"
167+
onSubmit={e => {
168+
e.preventDefault();
177169
addTraceId();
178170
}}
179171
>
180-
<PlusIcon className="size-4" />
181-
</Button>
182-
</form>
183-
{props.selectedValues.map(value => (
184-
<SidebarMenuButton
185-
key={value}
186-
onClick={() => props.onChange(props.selectedValues.filter(val => val !== value))}
187-
className="group/trace-id hover:bg-sidebar-accent/50"
188-
>
189-
<div
190-
data-active
191-
className="text-sidebar-primary-foreground border-sidebar-primary bg-sidebar-primary group-hover/trace-id:border-sidebar-border flex aspect-square size-4 shrink-0 items-center justify-center rounded-sm border group-hover/trace-id:bg-transparent"
172+
<FilterInput
173+
type="text"
174+
placeholder="Trace ID..."
175+
value={traceId}
176+
onChange={handleTraceIdChange}
177+
/>
178+
<Button
179+
variant="secondary"
180+
className="h-9 w-9 p-0"
181+
type="submit"
182+
onClick={() => {
183+
addTraceId();
184+
}}
192185
>
193-
<CheckIcon className="block size-3 group-hover/trace-id:hidden" />
194-
<MinusIcon className="hidden size-3 group-hover/trace-id:block" />
195-
</div>
196-
{value}
197-
</SidebarMenuButton>
198-
))}
199-
</FilterContent>
200-
</Filter>
201-
);
202-
}
186+
<PlusIcon className="size-4" />
187+
</Button>
188+
</form>
189+
{props.selectedValues.map(value => (
190+
<SidebarMenuButton
191+
key={value}
192+
onClick={() => props.onChange(props.selectedValues.filter(val => val !== value))}
193+
className="group/trace-id hover:bg-sidebar-accent/50"
194+
>
195+
<div
196+
data-active
197+
className="text-sidebar-primary-foreground border-sidebar-primary bg-sidebar-primary group-hover/trace-id:border-sidebar-border flex aspect-square size-4 shrink-0 items-center justify-center rounded-sm border group-hover/trace-id:bg-transparent"
198+
>
199+
<CheckIcon className="block size-3 group-hover/trace-id:hidden" />
200+
<MinusIcon className="hidden size-3 group-hover/trace-id:block" />
201+
</div>
202+
{value}
203+
</SidebarMenuButton>
204+
))}
205+
</FilterContent>
206+
</Filter>
207+
);
208+
},
209+
);
203210

204-
export function MultiSelectFilter<$Value>(props: {
211+
export const MultiSelectFilter = memo(function <$Value>(props: {
205212
name: string;
206213
/**
207214
* Filter's key for the backend and url state
@@ -270,7 +277,7 @@ export function MultiSelectFilter<$Value>(props: {
270277
</FilterContent>
271278
</Filter>
272279
);
273-
}
280+
});
274281

275282
function FilterOption(props: {
276283
onClick(): void;
@@ -333,81 +340,80 @@ const DoubleSlider = forwardRef<
333340
</SliderPrimitive.Root>
334341
));
335342

336-
export function DurationFilter(props: {
337-
value: [number, number] | [];
338-
onChange(value: [number, number]): void;
339-
}) {
340-
const minValue = 0;
341-
const maxValue = 100000;
342-
const defaultValues: [number, number] = [minValue, maxValue];
343-
const values: [number, number] = props.value.length ? props.value : defaultValues;
343+
export const DurationFilter = memo(
344+
(props: { value: [number, number] | []; onChange(value: [number, number]): void }) => {
345+
const minValue = 0;
346+
const maxValue = 100000;
347+
const defaultValues: [number, number] = [minValue, maxValue];
348+
const values: [number, number] = props.value.length ? props.value : defaultValues;
344349

345-
const handleSliderChange = (newValues: [number, number]) => {
346-
props.onChange(newValues);
347-
};
350+
const handleSliderChange = (newValues: [number, number]) => {
351+
props.onChange(newValues);
352+
};
348353

349-
const handleInputChange = (index: number, value: string) => {
350-
const numValue = Number.parseInt(value) || minValue;
351-
const newValues: [number, number] = [...values];
352-
newValues[index] = Math.min(Math.max(numValue, minValue), maxValue);
353-
props.onChange(newValues);
354-
};
354+
const handleInputChange = (index: number, value: string) => {
355+
const numValue = Number.parseInt(value) || minValue;
356+
const newValues: [number, number] = [...values];
357+
newValues[index] = Math.min(Math.max(numValue, minValue), maxValue);
358+
props.onChange(newValues);
359+
};
355360

356-
return (
357-
<Filter name="Duration">
358-
<FilterTitle
359-
changes={values[0] === minValue && values[1] === maxValue ? 0 : 1}
360-
children={'Duration'}
361-
onReset={() => props.onChange(defaultValues)}
362-
/>
363-
<FilterContent>
364-
<div className="space-y-6 p-2">
365-
<div className="space-y-2">
366-
<div className="space-y-1">
367-
<label className="font-mono text-xs text-zinc-400">MIN</label>
368-
<div className="relative">
369-
<FilterInput
370-
type="number"
371-
value={values[0]}
372-
onChange={e => handleInputChange(0, e.target.value)}
373-
className="h-7 border-zinc-800 bg-transparent px-2 pr-8 font-mono text-white"
374-
/>
375-
<span className="absolute right-2 top-1/2 -translate-y-1/2 font-mono text-xs text-zinc-400">
376-
ms
377-
</span>
361+
return (
362+
<Filter name="Duration">
363+
<FilterTitle
364+
changes={values[0] === minValue && values[1] === maxValue ? 0 : 1}
365+
children={'Duration'}
366+
onReset={() => props.onChange(defaultValues)}
367+
/>
368+
<FilterContent>
369+
<div className="space-y-6 p-2">
370+
<div className="space-y-2">
371+
<div className="space-y-1">
372+
<label className="font-mono text-xs text-zinc-400">MIN</label>
373+
<div className="relative">
374+
<FilterInput
375+
type="number"
376+
value={values[0]}
377+
onChange={e => handleInputChange(0, e.target.value)}
378+
className="h-7 border-zinc-800 bg-transparent px-2 pr-8 font-mono text-white"
379+
/>
380+
<span className="absolute right-2 top-1/2 -translate-y-1/2 font-mono text-xs text-zinc-400">
381+
ms
382+
</span>
383+
</div>
378384
</div>
379-
</div>
380-
<div className="space-y-1">
381-
<label className="font-mono text-xs text-zinc-400">MAX</label>
382-
<div className="relative">
383-
<FilterInput
384-
type="number"
385-
value={values[1]}
386-
onChange={e => handleInputChange(1, e.target.value)}
387-
className="h-7 border-gray-800 bg-transparent px-2 pr-8 font-mono text-white"
388-
/>
389-
<span className="absolute right-2 top-1/2 -translate-y-1/2 font-mono text-xs text-gray-400">
390-
ms
391-
</span>
385+
<div className="space-y-1">
386+
<label className="font-mono text-xs text-zinc-400">MAX</label>
387+
<div className="relative">
388+
<FilterInput
389+
type="number"
390+
value={values[1]}
391+
onChange={e => handleInputChange(1, e.target.value)}
392+
className="h-7 border-gray-800 bg-transparent px-2 pr-8 font-mono text-white"
393+
/>
394+
<span className="absolute right-2 top-1/2 -translate-y-1/2 font-mono text-xs text-gray-400">
395+
ms
396+
</span>
397+
</div>
392398
</div>
393399
</div>
400+
<DoubleSlider
401+
defaultValue={defaultValues}
402+
max={maxValue}
403+
min={minValue}
404+
step={1}
405+
value={values}
406+
onValueChange={handleSliderChange}
407+
className="[&_[role=slider]]:h-4 [&_[role=slider]]:w-4"
408+
/>
394409
</div>
395-
<DoubleSlider
396-
defaultValue={defaultValues}
397-
max={maxValue}
398-
min={minValue}
399-
step={1}
400-
value={values}
401-
onValueChange={handleSliderChange}
402-
className="[&_[role=slider]]:h-4 [&_[role=slider]]:w-4"
403-
/>
404-
</div>
405-
</FilterContent>
406-
</Filter>
407-
);
408-
}
410+
</FilterContent>
411+
</Filter>
412+
);
413+
},
414+
);
409415

410-
export function TimelineFilter() {
416+
export const TimelineFilter = memo(() => {
411417
const [selectedPreset, setSelectedPreset] = useState<string | null>(null);
412418
const [dateRange, setDateRange] = useState<DateRange | undefined>({
413419
from: addDays(new Date(), -3),
@@ -510,4 +516,4 @@ export function TimelineFilter() {
510516
</FilterContent>
511517
</Filter>
512518
);
513-
}
519+
});

0 commit comments

Comments
 (0)