Compare commits

..
Author SHA1 Message Date
jialinandjialin eb029a3e2a fix(table): smooth list empty/loading transitions, no layout jump on entry
Reserve a stable block for the empty/loading state (matching the NoResult
height) and fade the empty state in, so entering a list page no longer jumps
between spinner, empty result, and rows. Apply scroll-table to the antd main
lists and pass emptyMinHeight to the SealTable-based lists.
2026-07-15 14:05:19 +08:00
hibigandjialin 2dd287bbc1 chore: bump @gpustack/core-ui to v1.0.37 2026-07-15 13:43:46 +08:00
15 changed files with 46 additions and 4 deletions
+31
View File
@@ -9,6 +9,15 @@
} }
} }
@keyframes tableEmptyFadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.scroll-table { .scroll-table {
.ant-table { .ant-table {
.ant-table-container { .ant-table-container {
@@ -18,5 +27,27 @@
scrollbar-color: var(--color-scrollbar-thumb) transparent; scrollbar-color: var(--color-scrollbar-thumb) transparent;
} }
} }
// Reserve a stable block for the empty/loading state so the first-load
// spinner and the empty result occupy the same height as eventual data —
// this removes the layout jump when entering the page. Scoped to
// `.ant-table-content` so it only targets x-scroll tables (whose empty
// row lives here) and leaves fixed-height `scroll.y` tables untouched.
// Height must match the `minHeight` passed to <NoResult> in
// use-no-resource-result.
.ant-table-content {
.ant-table-placeholder {
> .ant-table-cell {
height: calc(100vh - 300px);
}
// NoResult renders nothing while loading and mounts an <Empty> only
// once the request settles, so this fires exactly when the empty
// state appears — a seamless fade-in instead of a hard pop.
.ant-empty {
animation: tableEmptyFadeIn 0.3s ease-in-out;
}
}
}
} }
} }
+1
View File
@@ -232,6 +232,7 @@ const APIKeys: React.FC = () => {
></FilterBar> ></FilterBar>
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
className={'scroll-table'}
columns={columns} columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowSelection={rowSelection} rowSelection={rowSelection}
@@ -395,6 +395,7 @@ const Clusters: React.FC = () => {
> >
<SealTable <SealTable
rowKey="id" rowKey="id"
emptyMinHeight="calc(100vh - 300px)"
loadChildren={getWorkerPoolList} loadChildren={getWorkerPoolList}
sortDirections={TABLE_SORT_DIRECTIONS} sortDirections={TABLE_SORT_DIRECTIONS}
expandedRowKeys={expandedRowKeys} expandedRowKeys={expandedRowKeys}
@@ -200,6 +200,7 @@ const Credentials: React.FC = () => {
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
className={'scroll-table'}
tableLayout="fixed" tableLayout="fixed"
columns={columns} columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
@@ -362,6 +362,7 @@ const GPUService: React.FC = () => {
/> />
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
className={'scroll-table'}
columns={columns} columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowSelection={rowSelection} rowSelection={rowSelection}
@@ -154,6 +154,7 @@ const GPUServicePublicKeys: React.FC = () => {
/> />
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
className={'scroll-table'}
columns={columns} columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowSelection={rowSelection} rowSelection={rowSelection}
@@ -150,6 +150,7 @@ const GPUServiceStorageTypes: React.FC = () => {
/> />
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
className={'scroll-table'}
columns={columns} columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowSelection={rowSelection} rowSelection={rowSelection}
+1
View File
@@ -151,6 +151,7 @@ const GPUServiceStorage: React.FC = () => {
/> />
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
className={'scroll-table'}
columns={columns} columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowSelection={rowSelection} rowSelection={rowSelection}
@@ -613,6 +613,7 @@ const Models: React.FC<ModelsProps> = ({
></PageTools> ></PageTools>
<SealTable <SealTable
columns={columns} columns={columns}
emptyMinHeight="calc(100vh - 300px)"
sortDirections={TABLE_SORT_DIRECTIONS} sortDirections={TABLE_SORT_DIRECTIONS}
dataSource={dataSource} dataSource={dataSource}
rowSelection={rowSelection} rowSelection={rowSelection}
+1
View File
@@ -159,6 +159,7 @@ const MaasProvider: React.FC = () => {
></FilterBar> ></FilterBar>
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
className={'scroll-table'}
rowKey="id" rowKey="id"
tableLayout="fixed" tableLayout="fixed"
sortDirections={TABLE_SORT_DIRECTIONS} sortDirections={TABLE_SORT_DIRECTIONS}
+1
View File
@@ -364,6 +364,7 @@ const ModelRoutes: React.FC = () => {
> >
<SealTable <SealTable
rowKey="id" rowKey="id"
emptyMinHeight="calc(100vh - 300px)"
loadChildren={loadChildrenData} loadChildren={loadChildrenData}
sortDirections={TABLE_SORT_DIRECTIONS} sortDirections={TABLE_SORT_DIRECTIONS}
expandedRowKeys={expandedRowKeys} expandedRowKeys={expandedRowKeys}
+2 -2
View File
@@ -123,14 +123,14 @@ const GPUList: React.FC<GPUListProps> = ({ clusterId, source }) => {
columns={columns} columns={columns}
sortDirections={TABLE_SORT_DIRECTIONS} sortDirections={TABLE_SORT_DIRECTIONS}
showSorterTooltip={false} showSorterTooltip={false}
tableLayout={'auto'} scroll={{ x: 'max-content' }}
className={'scroll-table'}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
loading={{ loading={{
spinning: dataSource.loading, spinning: dataSource.loading,
size: 'middle' size: 'middle'
}} }}
rowKey="id" rowKey="id"
scroll={{ x: 900 }}
onChange={handleTableChange} onChange={handleTableChange}
pagination={{ pagination={{
showSizeChanger: true, showSizeChanger: true,
@@ -340,6 +340,7 @@ const ModelFiles = () => {
></FilterBar> ></FilterBar>
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
className={'scroll-table'}
rowKey="id" rowKey="id"
tableLayout="fixed" tableLayout="fixed"
sortDirections={TABLE_SORT_DIRECTIONS} sortDirections={TABLE_SORT_DIRECTIONS}
+1 -2
View File
@@ -303,7 +303,7 @@ const Workers: React.FC<WorkersProps> = ({ clusterId, source }) => {
columns={columns} columns={columns}
sortDirections={TABLE_SORT_DIRECTIONS} sortDirections={TABLE_SORT_DIRECTIONS}
showSorterTooltip={false} showSorterTooltip={false}
tableLayout={'auto'} scroll={{ x: 'max-content' }}
className={'scroll-table'} className={'scroll-table'}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
loading={{ loading={{
@@ -311,7 +311,6 @@ const Workers: React.FC<WorkersProps> = ({ clusterId, source }) => {
size: 'middle' size: 'middle'
}} }}
rowKey="id" rowKey="id"
scroll={{ x: 900 }}
onChange={handleTableChange} onChange={handleTableChange}
rowSelection={source === 'clusterDetail' ? undefined : rowSelection} rowSelection={source === 'clusterDetail' ? undefined : rowSelection}
pagination={{ pagination={{
+1
View File
@@ -186,6 +186,7 @@ const Users: React.FC = () => {
></FilterBar> ></FilterBar>
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
className={'scroll-table'}
columns={columns} columns={columns}
dataSource={dataList} dataSource={dataList}
rowSelection={rowSelection} rowSelection={rowSelection}