perf: improve healthbar display, will responsive with container size

This commit is contained in:
moonrailgun 2024-08-18 03:58:10 +08:00
parent 6fecde0caa
commit 3990b0a872
6 changed files with 108 additions and 24 deletions

View File

@ -1,3 +1,4 @@
import { useResizeObserver } from '@/hooks/useResizeObserver';
import clsx from 'clsx';
import React from 'react';
@ -14,33 +15,46 @@ export interface HealthBarProps {
}
export const HealthBar: React.FC<HealthBarProps> = React.memo((props) => {
const size = props.size ?? 'small';
const [containerRef, containerRect] = useResizeObserver();
const cellCount = props.beats.length;
const cellNeedWidth = size === 'small' ? 8 : 12; // include gap
return (
<div
ref={containerRef}
className={clsx('flex', {
'gap-[3px]': size === 'small',
'gap-1': size === 'large',
})}
>
{props.beats.map((beat, i) => (
<div
key={i}
title={beat.title}
className={clsx(
'rounded-full transition-transform hover:scale-150',
{
'h-4 w-[5px]': size === 'small',
'h-8 w-2': size === 'large',
},
{
'bg-green-500': beat.status === 'health',
'bg-red-600': beat.status === 'error',
'bg-yellow-400': beat.status === 'warning',
'bg-gray-400': beat.status === 'none',
}
)}
/>
))}
{props.beats
.slice(
Math.floor(
Math.max(cellNeedWidth * cellCount - containerRect.width, 0) /
cellNeedWidth
),
cellCount
)
.map((beat, i) => (
<div
key={i}
title={beat.title}
className={clsx(
'rounded-full transition-transform hover:scale-150',
{
'h-4 w-[5px]': size === 'small',
'h-8 w-2': size === 'large',
},
{
'bg-green-500': beat.status === 'health',
'bg-red-600': beat.status === 'error',
'bg-yellow-400': beat.status === 'warning',
'bg-gray-400': beat.status === 'none',
}
)}
/>
))}
</div>
);
});

View File

@ -120,7 +120,7 @@ export const MonitorHealthBar: React.FC<MonitorHealthBarProps> = React.memo(
</span>
)}
<div className="flex-1">
<div className="flex-1 overflow-hidden">
<HealthBar size={size} beats={beats} />
</div>
@ -129,7 +129,7 @@ export const MonitorHealthBar: React.FC<MonitorHealthBarProps> = React.memo(
{last(beats)?.status === 'health' ? (
<div
className={clsx(
'rounded-full bg-green-500 px-4 py-1 text-lg font-bold text-white',
'ml-2 rounded-full bg-green-500 px-4 py-1 text-lg font-bold text-white',
{
'text-sm': size === 'small',
}
@ -140,7 +140,7 @@ export const MonitorHealthBar: React.FC<MonitorHealthBarProps> = React.memo(
) : last(beats)?.status === 'error' ? (
<div
className={clsx(
'rounded-full bg-red-600 px-4 py-1 text-lg font-bold text-white',
'ml-2 rounded-full bg-red-600 px-4 py-1 text-lg font-bold text-white',
{
'text-sm': size === 'small',
}
@ -151,7 +151,7 @@ export const MonitorHealthBar: React.FC<MonitorHealthBarProps> = React.memo(
) : (
<div
className={clsx(
'rounded-full bg-gray-400 px-4 py-1 text-lg font-bold text-white',
'ml-2 rounded-full bg-gray-400 px-4 py-1 text-lg font-bold text-white',
{
'text-sm': size === 'small',
}

View File

@ -1,4 +1,3 @@
import { CalendarOutlined } from '@ant-design/icons';
import dayjs, { Dayjs } from 'dayjs';
import { useMemo, useReducer } from 'react';
import { getMinimumUnit } from '@tianji/shared';

View File

@ -0,0 +1,70 @@
/**
* Copy from https://github.com/mantinedev/mantine/blob/master/packages/@mantine/hooks/src/use-resize-observer/use-resize-observer.ts
*/
import { useRef, useState, useMemo, useEffect } from 'react';
type ObserverRect = Omit<DOMRectReadOnly, 'toJSON'>;
const defaultState: ObserverRect = {
x: 0,
y: 0,
width: 0,
height: 0,
top: 0,
left: 0,
bottom: 0,
right: 0,
};
export function useResizeObserver<T extends HTMLElement = any>(
options?: ResizeObserverOptions
) {
const frameID = useRef(0);
const ref = useRef<T>(null);
const [rect, setRect] = useState<ObserverRect>(defaultState);
const observer = useMemo(
() =>
typeof window !== 'undefined'
? new ResizeObserver((entries: any) => {
const entry = entries[0];
if (entry) {
cancelAnimationFrame(frameID.current);
frameID.current = requestAnimationFrame(() => {
if (ref.current) {
setRect(entry.contentRect);
}
});
}
})
: null,
[]
);
useEffect(() => {
if (ref.current) {
observer?.observe(ref.current, options);
}
return () => {
observer?.disconnect();
if (frameID.current) {
cancelAnimationFrame(frameID.current);
}
};
}, [ref.current]);
return [ref, rect] as const;
}
export function useElementSize<T extends HTMLElement = any>(
options?: ResizeObserverOptions
) {
const [ref, { width, height }] = useResizeObserver<T>(options);
return { ref, width, height };
}

View File

@ -133,6 +133,7 @@ export const workspaceRouter = router({
workspaceId: z.string(),
})
)
.output(z.void())
.mutation(async ({ input, ctx }) => {
const { workspaceId } = input;
const userId = ctx.user.id;