feat: add card list component
This commit is contained in:
@@ -138,12 +138,12 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
icon={<DeleteOutlined />}
|
icon={<DeleteOutlined />}
|
||||||
danger
|
danger
|
||||||
onClick={handleDeleteByBatch}
|
onClick={handleDeleteByBatch}
|
||||||
disabled={!rowSelection.selectedRowKeys.length}
|
disabled={!rowSelection?.selectedRowKeys?.length}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{intl?.formatMessage?.({ id: 'common.button.delete' })}
|
{intl?.formatMessage?.({ id: 'common.button.delete' })}
|
||||||
{rowSelection.selectedRowKeys.length > 0 && (
|
{rowSelection?.selectedRowKeys?.length > 0 && (
|
||||||
<span>({rowSelection.selectedRowKeys?.length})</span>
|
<span>({rowSelection?.selectedRowKeys?.length})</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import breakpoints from '@/config/breakpoints';
|
||||||
|
import { Col, FloatButton, Row, Spin } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import ResizeObserver from 'rc-resize-observer';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const SpinWrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
max-height: 400px;
|
||||||
|
right: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface CatalogListProps {
|
||||||
|
dataList: any[];
|
||||||
|
loading: boolean;
|
||||||
|
activeId: number;
|
||||||
|
isFirst: boolean;
|
||||||
|
onDeploy: (data: any) => void;
|
||||||
|
renderItem: (data: any) => React.ReactNode;
|
||||||
|
Skeleton: React.ComponentType<{ span: number }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InnerSkeleton = (Skeleton: React.ComponentType<{ span: number }>) => {
|
||||||
|
return (props: any) => {
|
||||||
|
return <Skeleton {...props} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const ListSkeleton: React.FC<{
|
||||||
|
span: number;
|
||||||
|
loading: boolean;
|
||||||
|
isFirst: boolean;
|
||||||
|
Skeleton: React.ComponentType<{ span: number }>;
|
||||||
|
}> = ({ span, loading, isFirst, Skeleton }) => {
|
||||||
|
const CatalogSkeleton = InnerSkeleton(Skeleton);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{loading && (
|
||||||
|
<SpinWrapper>
|
||||||
|
<Spin
|
||||||
|
spinning={loading}
|
||||||
|
style={{
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
wrapperClassName="skelton-wrapper"
|
||||||
|
>
|
||||||
|
{isFirst && <CatalogSkeleton span={span}></CatalogSkeleton>}
|
||||||
|
</Spin>
|
||||||
|
</SpinWrapper>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CardList: React.FC<CatalogListProps> = (props) => {
|
||||||
|
const { dataList, loading, isFirst, Skeleton, renderItem } = props;
|
||||||
|
const [span, setSpan] = React.useState(8);
|
||||||
|
|
||||||
|
const getSpanByWidth = (width: number) => {
|
||||||
|
if (width < breakpoints.md) return 24;
|
||||||
|
if (width < breakpoints.lg) return 12;
|
||||||
|
return 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResize = useCallback(
|
||||||
|
_.throttle((size: { width: number; height: number }) => {
|
||||||
|
setSpan(getSpanByWidth(size.width));
|
||||||
|
}, 100),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative" style={{ width: '100%' }}>
|
||||||
|
<ResizeObserver onResize={handleResize}>
|
||||||
|
<div>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
{dataList.map((item: any, index) => {
|
||||||
|
return (
|
||||||
|
<Col span={span} key={item.id}>
|
||||||
|
{renderItem?.(item)}
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
<ListSkeleton
|
||||||
|
span={span}
|
||||||
|
loading={loading}
|
||||||
|
isFirst={isFirst}
|
||||||
|
Skeleton={Skeleton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ResizeObserver>
|
||||||
|
<FloatButton.BackTop visibilityHeight={1000} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CardList;
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
interface CardProps {
|
||||||
|
height?: string | number;
|
||||||
|
className?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CardWrapper = styled.div`
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
padding: 16px 20px;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
border: 1px solid var(--ant-color-border);
|
||||||
|
border-radius: var(--border-radius-base);
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--ant-color-fill-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: var(--ant-color-fill-tertiary);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Card: React.FC<CardProps> = (props) => {
|
||||||
|
const { className, height, children } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CardWrapper
|
||||||
|
className={classNames('card-wrapper', className)}
|
||||||
|
style={{ height: height || '180px' }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</CardWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Card;
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
|
import { FilterBar } from '@/components/page-tools';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const UserModels: React.FC = () => {
|
const UserModels: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
// Implement search functionality here
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputChange = () => {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer
|
<PageContainer
|
||||||
ghost
|
ghost
|
||||||
@@ -16,7 +24,18 @@ const UserModels: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
extra={[]}
|
extra={[]}
|
||||||
>
|
>
|
||||||
{intl.formatMessage({ id: 'menu.models.userModels' })}
|
<FilterBar
|
||||||
|
showSelect={true}
|
||||||
|
showPrimaryButton={false}
|
||||||
|
showDeleteButton={false}
|
||||||
|
selectHolder="Filter by cluster"
|
||||||
|
marginBottom={22}
|
||||||
|
marginTop={30}
|
||||||
|
buttonText={intl.formatMessage({ id: 'resources.button.create' })}
|
||||||
|
handleInputChange={() => {}}
|
||||||
|
handleSearch={handleSearch}
|
||||||
|
width={{ input: 200 }}
|
||||||
|
></FilterBar>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user