vaulterm/frontend/components/ui/grid-view.tsx
2024-11-18 01:28:31 +07:00

70 lines
1.7 KiB
TypeScript

import React, { useMemo } from "react";
import { GetProps, ScrollView, View, ViewStyle } from "tamagui";
type GridItem = { key: string };
type GridViewProps<T extends GridItem> = GetProps<typeof ScrollView> &
GridLayoutProps<T>;
const GridView = <T extends GridItem>({
data,
renderItem,
columns,
gap,
...props
}: GridViewProps<T>) => {
return (
<ScrollView {...props}>
<GridLayout
data={data}
renderItem={renderItem}
gap={gap}
columns={columns}
/>
</ScrollView>
);
};
type GridLayoutProps<T extends GridItem> = GetProps<typeof View> & {
data?: T[] | null;
renderItem: (item: T, index: number) => React.ReactNode;
gap?: string | number;
columns: {
xs?: number;
sm?: number;
md?: number;
lg?: number;
xl?: number;
};
};
export const GridLayout = <T extends GridItem>({
columns,
data,
renderItem,
gap,
...props
}: GridViewProps<T>) => {
const basisProps = useMemo(() => {
const basis: ViewStyle = { flexBasis: "100%" };
if (columns.xs) basis.flexBasis = `${100 / columns.xs}%`;
if (columns.sm) basis.$gtXs = { flexBasis: `${100 / columns.sm}%` };
if (columns.md) basis.$gtSm = { flexBasis: `${100 / columns.md}%` };
if (columns.lg) basis.$gtMd = { flexBasis: `${100 / columns.lg}%` };
if (columns.xl) basis.$gtLg = { flexBasis: `${100 / columns.xl}%` };
return basis;
}, [columns]);
return (
<View flexDirection="row" flexWrap="wrap" alignItems="stretch" {...props}>
{data?.map((item, idx) => (
<View key={item.key} p={gap} flexShrink={0} {...basisProps}>
{renderItem(item, idx)}
</View>
))}
</View>
);
};
export default GridView;