style: column wrapper srollable style

This commit is contained in:
jialin
2025-12-13 19:47:48 +08:00
parent b327eedb45
commit 7a0ee6aac4
15 changed files with 81 additions and 133 deletions
+32 -40
View File
@@ -1,8 +1,28 @@
import useOverlayScroller from '@/hooks/use-overlay-scroller';
import React, { useCallback } from 'react';
import './style.less';
import styled from 'styled-components';
import { WrapperContext } from './use-wrapper-context';
const Wrapper = styled.div`
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
width: 100%;
`;
const ContentWrapper = styled.div`
flex: 1;
position: relative;
overflow-y: auto;
`;
const Footer = styled.div`
padding-block: 0;
background-color: var(--ant-color-bg-elevated);
`;
interface ColumnWrapperProps {
children: React.ReactNode;
footer?: React.ReactNode;
@@ -44,29 +64,8 @@ const ColumnWrapper: React.FC<ColumnWrapperProps> = ({
}
}, []);
React.useLayoutEffect(() => {
if (!scroller.current || !footerRef.current || !contentRef.current) return;
const updatePadding = () => {
const height = footerRef.current!.getBoundingClientRect().height;
contentRef.current!.style.paddingBottom = `${height - 22}px`;
};
updatePadding();
const observer = new ResizeObserver(updatePadding);
observer.observe(footerRef.current);
return () => observer.disconnect();
}, []);
const setContentPaddingBottom = useCallback((padding: number) => {
if (footerRef.current) {
const height = footerRef.current.getBoundingClientRect().height;
contentRef.current!.style.paddingBottom = `${height + padding - 22}px`;
} else {
contentRef.current!.style.paddingBottom = `${padding}px`;
}
contentRef.current!.style.paddingBottom = `${padding}px`;
}, []);
return (
@@ -81,25 +80,18 @@ const ColumnWrapper: React.FC<ColumnWrapperProps> = ({
setSScrollContentPaddingBottom: setContentPaddingBottom
}}
>
<div
className="column-wrapper-footer"
style={{ height: maxHeight || '100%', ...styles.wrapper }}
>
<div
className="column-wrapper"
<Wrapper style={{ height: maxHeight || '100%', ...styles.wrapper }}>
<ContentWrapper
ref={scroller}
style={{ padding: '16px 24px', ...styles.container }}
style={{
padding: '16px 24px',
...styles.container
}}
>
<div ref={contentRef} className="child-content">
{children}
</div>
</div>
{footer && (
<div className="footer" ref={footerRef}>
{footer}
</div>
)}
</div>
<div ref={contentRef}>{children}</div>
</ContentWrapper>
{footer && <Footer ref={footerRef}>{footer}</Footer>}
</Wrapper>
</WrapperContext.Provider>
);
};
@@ -1,32 +0,0 @@
.column-wrapper {
flex: 1;
position: relative;
height: 100%;
overflow-y: auto;
}
.simplebar-scrollbar::before {
width: var(--scrollbar-size);
background: var(--scrollbar-handle-bg);
}
.column-wrapper-footer {
flex: 1;
display: flex;
position: relative;
width: 100%;
.column-wrapper {
border-left: none;
}
.footer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
padding-block: 0;
background-color: var(--ant-color-bg-elevated);
}
}
@@ -16,6 +16,12 @@ import { expirationOptions } from '../../config';
import { FormData, ListItem } from '../../config/types';
import APIKeyForm from './form';
const ModalFooterStyle = {
padding: '16px 24px 8px',
display: 'flex',
justifyContent: 'flex-end'
};
type AddModalProps = {
title: string;
action: PageActionType;
@@ -209,16 +215,15 @@ const AddModal: React.FC<AddModalProps> = ({
>
<ColumnWrapper
styles={{
container: { paddingTop: 0 }
container: { paddingBlock: 0 }
}}
footer={
!showKey ? (
<>
{isChanged && (
<div style={{ marginInline: 24 }}>
<div style={{ marginInline: 24, paddingTop: 8 }}>
<AlertBlockInfo
type="warning"
style={{ marginBottom: 16 }}
contentStyle={{ paddingInline: 0 }}
message={intl.formatMessage({
id: 'models.button.accessSettings.tips'
@@ -230,11 +235,7 @@ const AddModal: React.FC<AddModalProps> = ({
onOk={handleSumit}
onCancel={onCancel}
loading={loading}
style={{
padding: '16px 24px',
display: 'flex',
justifyContent: 'flex-end'
}}
style={ModalFooterStyle}
></ModalFooter>
</>
) : (
@@ -245,11 +246,7 @@ const AddModal: React.FC<AddModalProps> = ({
loading={loading}
okText={intl.formatMessage({ id: 'common.button.done' })}
showCancelBtn={false}
style={{
padding: '16px 24px',
display: 'flex',
justifyContent: 'flex-end'
}}
style={ModalFooterStyle}
></ModalFooter>
</>
)
+2 -2
View File
@@ -20,7 +20,7 @@ import BackendForm from '../forms';
import ImportYAML from './import-yaml';
const ModalFooterStyle = {
padding: '16px 24px',
padding: '16px 24px 8px',
display: 'flex',
justifyContent: 'flex-end'
};
@@ -220,7 +220,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
maxHeight={'calc(100vh - 140px)'}
styles={{
container: {
paddingTop: 0
paddingBlock: 0
}
}}
footer={
@@ -26,7 +26,7 @@ interface CompatibilityAlertProps {
const DivWrapper = styled.div`
position: relative;
padding-inline: 24px;
padding-block: 10px 0;
padding-block: 8px 0;
&:hover {
.close-wrapper {
display: block;
@@ -357,7 +357,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
<ColumnWrapper
styles={{
container: {
paddingTop: 0
paddingBlock: 0
}
}}
footer={
@@ -387,7 +387,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
)
}
style={{
padding: '16px 24px',
padding: '16px 24px 8px',
display: 'flex',
justifyContent: 'flex-end'
}}
@@ -41,7 +41,7 @@ const dropFieldsFromForm = [
const resetFields = ['worker_selector', 'env'];
const ModalFooterStyle = {
padding: '16px 24px',
padding: '16px 24px 8px',
display: 'flex',
justifyContent: 'flex-end'
};
@@ -586,7 +586,7 @@ const AddModal: FC<AddModalProps> = (props) => {
<FormWrapper>
<ColumnWrapper
styles={{
container: { paddingTop: 0 }
container: { paddingBlock: 0 }
}}
footer={
<>
@@ -35,7 +35,7 @@ type AddModalProps = {
};
const ModalFooterStyle = {
padding: '16px 24px',
padding: '16px 24px 8px',
display: 'flex',
justifyContent: 'flex-end'
};
@@ -249,7 +249,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
<ColumnWrapper
styles={{
container: {
paddingTop: 0
paddingBlock: 0
}
}}
footer={
@@ -490,6 +490,7 @@ const options: BackendParameter[] = [
'quark',
'moe_wna16',
'torchao',
'ascend',
'None'
]
},
+1 -1
View File
@@ -375,7 +375,7 @@ export const defaultFormValues = {
gpu_selector: {},
worker_selector: {},
backend_parameters: [],
backend_version: ''
backend_version: null
};
export const getBackendParamsTips = (backend: string) => {
+2 -2
View File
@@ -207,7 +207,7 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
<FormWrapper>
<ColumnWrapper
styles={{
container: { paddingTop: 0 }
container: { paddingBlock: 0 }
}}
footer={
<>
@@ -218,7 +218,7 @@ const DownloadModel: React.FC<AddModalProps> = (props) => {
loading: loading
}}
style={{
padding: '16px 24px',
padding: '16px 24px 8px',
display: 'flex',
justifyContent: 'flex-end'
}}
+20 -1
View File
@@ -118,6 +118,11 @@ const BackendFields: React.FC = () => {
return option.title;
};
const backendVersionLabelRender = (option: any) => {
console.log('backendVersionLabelRender option:', option);
return option.title;
};
const renderVersionOptions = (values: any[], label: string) => {
if (!values || values.length === 0) {
return null;
@@ -188,7 +193,8 @@ const BackendFields: React.FC = () => {
<SealSelect
allowClear
showSearch
labelRender={labelRender}
allowNull
labelRender={backendVersionLabelRender}
placeholder={intl.formatMessage({
id: 'models.form.backendVersion.holder'
})}
@@ -216,6 +222,19 @@ const BackendFields: React.FC = () => {
</dl>
}
>
{(backendVersions.builtIn.length > 0 ||
backendVersions.custom.length > 0) && (
<Select.Option
key="auto"
value={null}
title={intl.formatMessage({ id: 'common.options.auto' })}
label={intl.formatMessage({
id: 'common.options.auto'
})}
>
{intl.formatMessage({ id: 'common.options.auto' })}
</Select.Option>
)}
{renderVersionOptions(
backendVersions.builtIn,
intl.formatMessage({ id: 'backend.builtin' })
+4 -6
View File
@@ -95,8 +95,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
onClusterChange,
onOk
} = props;
const { getScrollElementScrollableHeight, osInstance, scrollEventElement } =
useWrapperContext();
const { getScrollElementScrollableHeight } = useWrapperContext();
const { backendOptions, getBackendOptions } = useQueryBackends();
const { getGPUOptionList, gpuOptions, workerLabelOptions } =
useGenerateGPUOptions();
@@ -105,8 +104,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
const [activeKey, setActiveKey] = React.useState<string[]>([]);
const [target, setTarget] = React.useState<string>(TABKeysMap.BASIC);
console.log('scroller instance in form:', osInstance, scrollEventElement);
const segmentOptions = [
{
value: TABKeysMap.BASIC,
@@ -226,7 +223,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
});
form.setFieldsValue({
env: null,
backend_version: '', // don't set default version here, let the user select it
backend_version: null, // don't set default version here, let the user select it
backend_parameters: option.default_backend_param || [],
...updateKVCacheConfig(val, option),
...updateGPUSelector(val)
@@ -454,7 +451,8 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
ngram_max_match_length:
initialValues?.speculative_config?.ngram_max_match_length || 10
},
...initialValues
...initialValues,
backend_version: initialValues?.backend_version || null
}}
>
<BasicForm
+1 -29
View File
@@ -4,7 +4,6 @@ import SealCascader from '@/components/seal-form/seal-cascader';
import SealSelect from '@/components/seal-form/seal-select';
import TooltipList from '@/components/tooltip-list';
import useAppUtils from '@/hooks/use-app-utils';
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
import { useIntl } from '@umijs/max';
import { Form, InputNumber } from 'antd';
import _ from 'lodash';
@@ -72,8 +71,6 @@ const ScheduleTypeForm: React.FC = () => {
clearCacheFormValues,
initialValues
} = useFormContext();
const { setSScrollContentPaddingBottom, getScrollElementScrollableHeight } =
useWrapperContext();
const { getRuleMessage } = useAppUtils();
const form = Form.useFormInstance();
const scheduleType = Form.useWatch('scheduleType', form);
@@ -82,29 +79,6 @@ const ScheduleTypeForm: React.FC = () => {
['gpu_selector', 'gpus_per_replica'],
form
);
const gpuSekectorElRef = React.useRef<HTMLElement | null>(null);
const setContentPaddingBottom = () => {
if (!gpuSekectorElRef.current) {
gpuSekectorElRef.current = document.querySelector(
'[data-field="gpu_selector.gpu_ids"]'
);
}
if (gpuSekectorElRef.current) {
const { scrollHeight, scrollTop } =
getScrollElementScrollableHeight?.() || {
scrollHeight: 0,
scrollTop: 0
};
const rect = gpuSekectorElRef.current.getBoundingClientRect();
const bottomSpace =
window.innerHeight - rect.bottom + (scrollHeight - scrollTop);
if (bottomSpace < 340) {
setSScrollContentPaddingBottom?.(340 - bottomSpace);
}
}
};
const handleScheduleTypeChange = async (value: string) => {
await new Promise((resolve) => {
@@ -118,11 +92,9 @@ const ScheduleTypeForm: React.FC = () => {
gpu_ids: [],
gpus_per_replica: null
});
requestAnimationFrame(() => {
setContentPaddingBottom();
});
}
};
const handleGpusPerReplicasChange = (val: string | number | null) => {
if (val === null) {
form.setFieldValue(['gpu_selector', 'gpus_per_replica'], null);
@@ -43,6 +43,7 @@ export default function useChatCompletion(
};
const joinMessage = (chunk: any) => {
console.log('chunk:', chunk);
setTokenResult({
...(chunk?.usage ?? {})
});