chore: catalog: scroll to load more data
This commit is contained in:
@@ -28,7 +28,6 @@ const HighlightCode: React.FC<{
|
|||||||
|
|
||||||
const currentTheme = React.useMemo(() => {
|
const currentTheme = React.useMemo(() => {
|
||||||
const res = theme || userSettings.theme === 'realDark' ? 'dark' : 'light';
|
const res = theme || userSettings.theme === 'realDark' ? 'dark' : 'light';
|
||||||
console.log('currentTheme:', res, userSettings.theme, '===>', theme);
|
|
||||||
return res;
|
return res;
|
||||||
}, [theme, userSettings.theme]);
|
}, [theme, userSettings.theme]);
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ export default {
|
|||||||
borderRadiusSM: 2,
|
borderRadiusSM: 2,
|
||||||
colorBgContainer: '#fff',
|
colorBgContainer: '#fff',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
motion: true,
|
motion: true
|
||||||
colorFillTertiary: '#f4f5f4'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export default function useBodyScroll() {
|
|||||||
window.__GPUSTACK_BODY_SCROLLER__?.elements()?.scrollEventElement;
|
window.__GPUSTACK_BODY_SCROLLER__?.elements()?.scrollEventElement;
|
||||||
|
|
||||||
instanceRef.current = window.__GPUSTACK_BODY_SCROLLER__;
|
instanceRef.current = window.__GPUSTACK_BODY_SCROLLER__;
|
||||||
|
console.log('bodyScroller', bodyScroller.current, instanceRef.current);
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveScrollHeight = React.useCallback(() => {
|
const saveScrollHeight = React.useCallback(() => {
|
||||||
|
|||||||
@@ -10,12 +10,18 @@ import { Button, Input, Pagination, Select, Space, message } from 'antd';
|
|||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import { createModel, queryCatalogList } from './apis';
|
import { createModel, queryCatalogList } from './apis';
|
||||||
import CatalogList from './components/catalog-list';
|
import CatalogList from './components/catalog-list';
|
||||||
import DelopyBuiltInModal from './components/deploy-builtin-modal';
|
import DelopyBuiltInModal from './components/deploy-builtin-modal';
|
||||||
import { modelCategories, modelSourceMap } from './config';
|
import { modelCategories, modelSourceMap } from './config';
|
||||||
import { CatalogItem as CatalogItemType, FormData } from './config/types';
|
import { CatalogItem as CatalogItemType, FormData } from './config/types';
|
||||||
|
|
||||||
|
const PageWrapper = styled.div`
|
||||||
|
display: none;
|
||||||
|
margin-block: 32px 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
const Catalog: React.FC = () => {
|
const Catalog: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||||
@@ -26,14 +32,16 @@ const Catalog: React.FC = () => {
|
|||||||
dataList: CatalogItemType[];
|
dataList: CatalogItemType[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
total: number;
|
total: number;
|
||||||
|
totalPage: number;
|
||||||
}>({
|
}>({
|
||||||
dataList: [],
|
dataList: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
total: 0
|
total: 0,
|
||||||
|
totalPage: 0
|
||||||
});
|
});
|
||||||
const [queryParams, setQueryParams] = useState({
|
const [queryParams, setQueryParams] = useState({
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 100,
|
perPage: 12,
|
||||||
search: '',
|
search: '',
|
||||||
categories: ''
|
categories: ''
|
||||||
});
|
});
|
||||||
@@ -71,39 +79,62 @@ const Catalog: React.FC = () => {
|
|||||||
[cacheData.current]
|
[cacheData.current]
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchData = useCallback(async () => {
|
const fetchData = useCallback(
|
||||||
setDataSource((pre) => {
|
async (query?: any) => {
|
||||||
pre.loading = true;
|
const searchQuery = {
|
||||||
return { ...pre };
|
...queryParams,
|
||||||
});
|
...query
|
||||||
try {
|
|
||||||
const params = {
|
|
||||||
..._.pick(queryParams, ['page', 'perPage'])
|
|
||||||
};
|
};
|
||||||
const res: any = await queryCatalogList(params);
|
if (
|
||||||
|
dataSource.loading ||
|
||||||
|
(searchQuery.page > dataSource.totalPage && dataSource.totalPage > 0)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setDataSource((pre) => {
|
||||||
|
pre.loading = true;
|
||||||
|
|
||||||
cacheData.current = res.items || [];
|
return { ...pre };
|
||||||
const dataList = filterData({
|
|
||||||
search: queryParams.search,
|
|
||||||
categories: queryParams.categories
|
|
||||||
});
|
});
|
||||||
setDataSource({
|
try {
|
||||||
dataList: dataList,
|
const params = {
|
||||||
loading: false,
|
..._.pickBy(searchQuery, (val: string | number) => !!val)
|
||||||
total: res.pagination.total
|
};
|
||||||
});
|
const res: any = await queryCatalogList(params);
|
||||||
} catch (error) {
|
|
||||||
cacheData.current = [];
|
const dataList =
|
||||||
setDataSource({
|
searchQuery.page === 1
|
||||||
dataList: [],
|
? res.items
|
||||||
loading: false,
|
: _.concat(dataSource.dataList, res.items);
|
||||||
total: dataSource.total
|
setDataSource({
|
||||||
});
|
dataList: dataList,
|
||||||
console.log('error', error);
|
loading: false,
|
||||||
} finally {
|
total: res.pagination.total,
|
||||||
setIsFirst(false);
|
totalPage: res.pagination.totalPage
|
||||||
}
|
});
|
||||||
}, [queryParams, cacheData.current]);
|
setQueryParams({
|
||||||
|
...queryParams,
|
||||||
|
...query
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
cacheData.current = [];
|
||||||
|
setDataSource({
|
||||||
|
dataList: [],
|
||||||
|
loading: false,
|
||||||
|
total: dataSource.total,
|
||||||
|
totalPage: dataSource.totalPage
|
||||||
|
});
|
||||||
|
setQueryParams({
|
||||||
|
...queryParams,
|
||||||
|
...query
|
||||||
|
});
|
||||||
|
console.log('error', error);
|
||||||
|
} finally {
|
||||||
|
setIsFirst(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[queryParams, cacheData.current]
|
||||||
|
);
|
||||||
|
|
||||||
const handleDeployModalCancel = () => {
|
const handleDeployModalCancel = () => {
|
||||||
setOpenDeployModal({
|
setOpenDeployModal({
|
||||||
@@ -160,49 +191,57 @@ const Catalog: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleSearch = (e: any) => {
|
const handleSearch = (e: any) => {
|
||||||
fetchData();
|
fetchData({
|
||||||
|
...queryParams,
|
||||||
|
page: 1
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNameChange = _.debounce((e: any) => {
|
const handleNameChange = _.debounce((e: any) => {
|
||||||
const dataList = filterData({
|
fetchData({
|
||||||
search: e.target.value,
|
|
||||||
categories: queryParams.categories
|
|
||||||
});
|
|
||||||
|
|
||||||
setQueryParams({
|
|
||||||
...queryParams,
|
...queryParams,
|
||||||
page: 1,
|
page: 1,
|
||||||
search: e.target.value
|
search: e.target.value
|
||||||
});
|
});
|
||||||
|
|
||||||
setDataSource({
|
|
||||||
dataList,
|
|
||||||
loading: false,
|
|
||||||
total: dataSource.total
|
|
||||||
});
|
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
const handleCategoryChange = (value: any) => {
|
const handleCategoryChange = (value: any) => {
|
||||||
const dataList = filterData({
|
fetchData({
|
||||||
search: queryParams.search,
|
|
||||||
categories: value
|
|
||||||
});
|
|
||||||
setQueryParams({
|
|
||||||
...queryParams,
|
...queryParams,
|
||||||
page: 1,
|
page: 1,
|
||||||
categories: value
|
categories: value
|
||||||
});
|
});
|
||||||
setDataSource({
|
|
||||||
dataList,
|
|
||||||
loading: false,
|
|
||||||
total: dataSource.total
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = async () => {
|
||||||
|
// Determine the scrolling element
|
||||||
|
const scrollingElement = document.documentElement || document.body;
|
||||||
|
|
||||||
|
// Calculate if the user has scrolled to the bottom
|
||||||
|
const isAtBottom =
|
||||||
|
scrollingElement.scrollTop + scrollingElement.clientHeight >=
|
||||||
|
scrollingElement.scrollHeight - 20; // Adding a small buffer for precision
|
||||||
|
|
||||||
|
if (isAtBottom) {
|
||||||
|
fetchData({
|
||||||
|
...queryParams,
|
||||||
|
page: queryParams.page + 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('scroll', handleScroll);
|
||||||
|
};
|
||||||
|
}, [fetchData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer
|
<PageContainer
|
||||||
ghost
|
ghost
|
||||||
@@ -259,7 +298,7 @@ const Catalog: React.FC = () => {
|
|||||||
activeId={-1}
|
activeId={-1}
|
||||||
isFirst={isFirst}
|
isFirst={isFirst}
|
||||||
></CatalogList>
|
></CatalogList>
|
||||||
<div style={{ marginBlock: '32px 16px' }}>
|
<PageWrapper>
|
||||||
<Pagination
|
<Pagination
|
||||||
hideOnSinglePage={queryParams.perPage === 100}
|
hideOnSinglePage={queryParams.perPage === 100}
|
||||||
align="end"
|
align="end"
|
||||||
@@ -269,7 +308,7 @@ const Catalog: React.FC = () => {
|
|||||||
showSizeChanger
|
showSizeChanger
|
||||||
onChange={handleOnPageChange}
|
onChange={handleOnPageChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</PageWrapper>
|
||||||
<DelopyBuiltInModal
|
<DelopyBuiltInModal
|
||||||
open={openDeployModal.show}
|
open={openDeployModal.show}
|
||||||
action={PageAction.CREATE}
|
action={PageAction.CREATE}
|
||||||
|
|||||||
@@ -3,10 +3,23 @@ import { Col, FloatButton, Row, Spin } from 'antd';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import ResizeObserver from 'rc-resize-observer';
|
import ResizeObserver from 'rc-resize-observer';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import { CatalogItem as CatalogItemType } from '../config/types';
|
import { CatalogItem as CatalogItemType } from '../config/types';
|
||||||
import CatalogItem from './catalog-item';
|
import CatalogItem from './catalog-item';
|
||||||
import CatalogSkelton from './catalog-skelton';
|
import CatalogSkelton from './catalog-skelton';
|
||||||
|
|
||||||
|
const SpinWrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 400px;
|
||||||
|
right: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
interface CatalogListProps {
|
interface CatalogListProps {
|
||||||
dataList: any[];
|
dataList: any[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@@ -23,19 +36,7 @@ const ListSkeleton: React.FC<{
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{loading && (
|
{loading && (
|
||||||
<div
|
<SpinWrapper>
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
position: 'absolute',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
justifyContent: 'center',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
height: 400,
|
|
||||||
right: 0
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Spin
|
<Spin
|
||||||
spinning={loading}
|
spinning={loading}
|
||||||
style={{
|
style={{
|
||||||
@@ -45,7 +46,7 @@ const ListSkeleton: React.FC<{
|
|||||||
>
|
>
|
||||||
{isFirst && <CatalogSkelton span={span}></CatalogSkelton>}
|
{isFirst && <CatalogSkelton span={span}></CatalogSkelton>}
|
||||||
</Spin>
|
</Spin>
|
||||||
</div>
|
</SpinWrapper>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnValuesChange = (data: any) => {
|
const handleOnValuesChange = _.debounce((data: any) => {
|
||||||
const formdata = form.getFieldsValue?.();
|
const formdata = form.getFieldsValue?.();
|
||||||
|
|
||||||
let alldata = {};
|
let alldata = {};
|
||||||
@@ -116,7 +116,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const originalData = _.pick(originFormData.current, Object.keys(alldata));
|
const originalData = _.pick(originFormData.current, Object.keys(alldata));
|
||||||
console.log('alldata:', alldata, originalData);
|
console.log('alldata:', formdata, alldata, originalData);
|
||||||
|
|
||||||
const isEqual = _.isEqualWith(
|
const isEqual = _.isEqualWith(
|
||||||
_.omit(alldata, updateIgnoreFields),
|
_.omit(alldata, updateIgnoreFields),
|
||||||
@@ -137,7 +137,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}, 100);
|
||||||
|
|
||||||
// voxbox is not support multi gpu
|
// voxbox is not support multi gpu
|
||||||
const handleSetGPUIds = (backend: string) => {
|
const handleSetGPUIds = (backend: string) => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// this list is copied from llamacpp repo
|
||||||
enum FileType {
|
enum FileType {
|
||||||
F32 = 0,
|
F32 = 0,
|
||||||
F16 = 1,
|
F16 = 1,
|
||||||
|
|||||||
@@ -407,10 +407,13 @@ const GroundEmbedding: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
key: 'json',
|
key: 'json',
|
||||||
label: 'JSON',
|
label: 'JSON',
|
||||||
children: (
|
children: (
|
||||||
<div style={{ padding: 10, backgroundColor: '#fafafa' }}>
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--ant-color-bg-container)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<HighlightCode
|
<HighlightCode
|
||||||
height={outputHeight - 20}
|
height={outputHeight - 32}
|
||||||
theme="light"
|
|
||||||
code={embeddingData.code}
|
code={embeddingData.code}
|
||||||
copyValue={embeddingData.copyValue}
|
copyValue={embeddingData.copyValue}
|
||||||
lang="json"
|
lang="json"
|
||||||
@@ -671,9 +674,10 @@ const GroundEmbedding: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
style={{
|
style={{
|
||||||
border: '1px solid var(--ant-color-border)',
|
border: '1px solid var(--ant-color-border)',
|
||||||
borderRadius: 'var(--border-radius-base)',
|
borderRadius: 'var(--border-radius-base)',
|
||||||
width: '100%'
|
width: '100%',
|
||||||
|
overflow: 'hidden'
|
||||||
}}
|
}}
|
||||||
className="scatter "
|
className="scatter"
|
||||||
>
|
>
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultActiveKey={outputType}
|
defaultActiveKey={outputType}
|
||||||
|
|||||||
Reference in New Issue
Block a user