Skip to content

Commit

Permalink
♻️ refactor: Update format utils and shared layout (lobehub#4431)
Browse files Browse the repository at this point in the history
* 🔧 chore: Update format utils

* 💄 style: Update layout
  • Loading branch information
canisminor1990 authored Oct 21, 2024
1 parent fc1d0a6 commit 56ed073
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 19 deletions.
3 changes: 2 additions & 1 deletion src/components/server/MobileNavLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ const MobileContentLayout = ({
withNav,
style,
header,
id = 'lobe-mobile-scroll-container',
...rest
}: MobileContentLayoutProps) => {
const content = (
<Flexbox
height="100%"
id={'lobe-mobile-scroll-container'}
id={id}
style={{
overflowX: 'hidden',
overflowY: 'auto',
Expand Down
23 changes: 17 additions & 6 deletions src/features/Setting/SettingContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,41 @@

import { useResponsive } from 'antd-style';
import { PropsWithChildren, ReactNode, memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import { Flexbox, FlexboxProps } from 'react-layout-kit';

interface SettingContainerProps {
interface SettingContainerProps extends FlexboxProps {
addonAfter?: ReactNode;
addonBefore?: ReactNode;
fullWidth?: boolean;

maxWidth?: number;
}
const SettingContainer = memo<PropsWithChildren<SettingContainerProps>>(
({ children, addonAfter, addonBefore, fullWidth }) => {
({
id = 'lobe-desktop-scroll-container',
maxWidth = 1024,
children,
addonAfter,
addonBefore,
style,
...rest
}) => {
const { mobile = false } = useResponsive();
return (
<Flexbox
align={'center'}
height={'100%'}
id={id}
paddingBlock={mobile ? undefined : 32}
style={{ overflowX: 'hidden', overflowY: 'auto' }}
style={{ overflowX: 'hidden', overflowY: 'auto', ...style }}
width={'100%'}
{...rest}
>
{addonBefore}
<Flexbox
gap={64}
paddingInline={mobile ? undefined : 24}
style={{
maxWidth: fullWidth ? undefined : 1024,
maxWidth,
}}
width={'100%'}
>
Expand Down
68 changes: 66 additions & 2 deletions src/utils/format.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import dayjs from 'dayjs';
import { describe, expect, it } from 'vitest';

import { CNY_TO_USD } from '@/const/discover';

import {
formatDate,
formatIntergerNumber,
formatNumber,
formatPrice,
formatPriceByCurrency,
Expand Down Expand Up @@ -37,6 +42,15 @@ describe('format', () => {
expect(formatSize(1023)).toBe('1.0 KB');
expect(formatSize(1073741823)).toBe('1024.0 MB');
});

it('should handle undefined input', () => {
expect(formatSize(undefined as any)).toBe('--');
});

it('should use custom fraction digits', () => {
expect(formatSize(1536, 2)).toBe('1.50 KB');
expect(formatSize(1572864, 3)).toBe('1.500 MB');
});
});

describe('formatSpeed', () => {
Expand All @@ -60,6 +74,14 @@ describe('format', () => {
expect(formatSpeed(1000 * 1024)).toBe('1000.00 KB/s');
expect(formatSpeed(1000.01 * 1024)).toBe('0.98 MB/s');
});

it('should handle undefined input', () => {
expect(formatSpeed(undefined as any)).toBe('--');
});

it('should use custom fraction digits', () => {
expect(formatSpeed(1024, 3)).toBe('1.000 KB/s');
});
});

describe('formatTime', () => {
Expand Down Expand Up @@ -88,12 +110,16 @@ describe('format', () => {
expect(formatTime(59.99)).toBe('60.0 s');
expect(formatTime(3599.99)).toBe('60.0 min');
});
it('should handle non-number inputs', () => {
expect(formatTime('not a number' as any)).toBe('not a number');
expect(formatTime(undefined as any)).toBe('--');
});
});

describe('formatShortenNumber', () => {
it('should return the input if it is not a number', () => {
expect(formatShortenNumber('not a number')).toBe('not a number');
expect(formatShortenNumber(null)).toBe(null);
expect(formatShortenNumber(null)).toBe('--');
});

it('should format numbers less than 10,000 correctly', () => {
Expand Down Expand Up @@ -125,7 +151,22 @@ describe('format', () => {
it('should handle non-number inputs', () => {
expect(formatNumber('1000')).toBe('1,000');
expect(formatNumber('not a number')).toBe(Number.NaN.toString());
expect(formatNumber(null)).toBe(undefined);
expect(formatNumber(0)).toBe('0');
expect(formatNumber(0, 1)).toBe('0.0');
expect(formatNumber(null)).toBe('--');
});

it('should handle fraction digits correctly', () => {
expect(formatNumber(1234.5678, 2)).toBe('1,234.57');
expect(formatNumber(1234.5678, 3)).toBe('1,234.568');
});
});

describe('formatIntergerNumber', () => {
it('should format numbers with commas correctly', () => {
expect(formatIntergerNumber(1000.12)).toBe('1,000');
expect(formatIntergerNumber(0)).toBe('0');
expect(formatIntergerNumber(null)).toBe('--');
});
});

Expand All @@ -150,6 +191,12 @@ describe('format', () => {
expect(formatPriceByCurrency(1000, 'CNY')).toBe('140.06');
expect(formatPriceByCurrency(6500, 'CNY')).toBe('910.36');
});

it('should use the correct CNY_TO_USD conversion rate', () => {
const price = 1000;
const expectedCNY = formatPrice(price / CNY_TO_USD);
expect(formatPriceByCurrency(price, 'CNY')).toBe(expectedCNY);
});
});

describe('formatTokenNumber', () => {
Expand Down Expand Up @@ -192,4 +239,21 @@ describe('format', () => {
expect(formatTokenNumber(2097152)).toBe('2M'); // Gemini Pro
});
});

describe('formatDate', () => {
it('should format date correctly', () => {
const date = new Date('2023-05-15T12:00:00Z');
expect(formatDate(date)).toBe('2023-05-15');
});

it('should handle undefined input', () => {
expect(formatDate(undefined)).toBe('--');
});

it('should use dayjs for formatting', () => {
const date = new Date('2023-05-15T12:00:00Z');
const expectedFormat = dayjs(date).format('YYYY-MM-DD');
expect(formatDate(date)).toBe(expectedFormat);
});
});
});
45 changes: 35 additions & 10 deletions src/utils/format.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import dayjs from 'dayjs';
import { isNumber } from 'lodash-es';
import numeral from 'numeral';

import { CNY_TO_USD } from '@/const/discover';
import { ModelPriceCurrency } from '@/types/llm';

export const formatSize = (bytes: number, fractionDigits = 1): string => {
export const formatSize = (bytes: number, fractionDigits: number = 1): string => {
if (!bytes && bytes !== 0) return '--';

const kbSize = bytes / 1024;
if (kbSize < 1024) {
return `${kbSize.toFixed(fractionDigits)} KB`;
Expand All @@ -21,6 +24,8 @@ export const formatSize = (bytes: number, fractionDigits = 1): string => {
* format speed from Byte number to string like KB/s, MB/s or GB/s
*/
export const formatSpeed = (byte: number, fractionDigits = 2) => {
if (!byte && byte !== 0) return '--';

let word = '';

// Byte
Expand All @@ -44,6 +49,9 @@ export const formatSpeed = (byte: number, fractionDigits = 2) => {
};

export const formatTime = (timeInSeconds: number): string => {
if (!timeInSeconds && timeInSeconds !== 0) return '--';
if (!isNumber(timeInSeconds)) return timeInSeconds;

if (timeInSeconds < 60) {
return `${timeInSeconds.toFixed(1)} s`;
} else if (timeInSeconds < 3600) {
Expand All @@ -54,7 +62,9 @@ export const formatTime = (timeInSeconds: number): string => {
};

export const formatShortenNumber = (num: any) => {
if (!num && num !== 0) return '--';
if (!isNumber(num)) return num;

// 使用Intl.NumberFormat来添加千分号
const formattedWithComma = new Intl.NumberFormat('en-US').format(num);

Expand All @@ -70,16 +80,23 @@ export const formatShortenNumber = (num: any) => {
}
};

/**
* format number with comma
* @param num
*/
export const formatNumber = (num: any) => {
if (!num) return;
return new Intl.NumberFormat('en-US').format(num);
export const formatNumber = (num: any, fractionDigits?: number) => {
if (!num && num !== 0) return '--';

if (!fractionDigits) return new Intl.NumberFormat('en-US').format(num);
const [a, b] = num.toFixed(fractionDigits).split('.');
return `${numeral(a).format('0,0')}.${b}`;
};

export const formatIntergerNumber = (num: any) => {
if (!num && num !== 0) return '--';

return numeral(num).format('0,0');
};

export const formatTokenNumber = (num: number): string => {
if (!num && num !== 0) return '--';

if (num > 0 && num < 1024) return '1K';

let kiloToken = Math.floor(num / 1024);
Expand All @@ -90,8 +107,10 @@ export const formatTokenNumber = (num: number): string => {
return kiloToken < 1000 ? `${kiloToken}K` : `${Math.floor(kiloToken / 1000)}M`;
};

export const formatPrice = (price: number) => {
const [a, b] = price.toFixed(2).split('.');
export const formatPrice = (price: number, fractionDigits: number = 2) => {
if (!price && price !== 0) return '--';

const [a, b] = price.toFixed(fractionDigits).split('.');
return `${numeral(a).format('0,0')}.${b}`;
};

Expand All @@ -101,3 +120,9 @@ export const formatPriceByCurrency = (price: number, currency?: ModelPriceCurren
}
return formatPrice(price);
};

export const formatDate = (date?: Date) => {
if (!date) return '--';

return dayjs(date).format('YYYY-MM-DD');
};

0 comments on commit 56ed073

Please sign in to comment.