style: transition wrapper height
This commit is contained in:
@@ -23,6 +23,18 @@
|
|||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: height 300ms ease-in-out;
|
transition: all 300ms ease-in-out;
|
||||||
|
.content {
|
||||||
|
height: max-content;
|
||||||
|
background-color: var(--color-white-1);
|
||||||
|
border-radius: var(--border-radius-base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-input {
|
||||||
|
/* 只隐藏垂直滚动条轨道 */
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
width: 0px !important;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import {
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
interface TransitionWrapProps {
|
interface TransitionWrapProps {
|
||||||
@@ -7,50 +13,67 @@ interface TransitionWrapProps {
|
|||||||
header?: React.ReactNode;
|
header?: React.ReactNode;
|
||||||
variant?: 'bordered' | 'filled';
|
variant?: 'bordered' | 'filled';
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
ref?: any;
|
||||||
}
|
}
|
||||||
const TransitionWrapper: React.FC<TransitionWrapProps> = (props) => {
|
const TransitionWrapper: React.FC<TransitionWrapProps> = forwardRef(
|
||||||
const { minHeight = 50, header, variant = 'bordered', children } = props;
|
(props, ref) => {
|
||||||
const [isOpen, setIsOpen] = useState(true);
|
const { minHeight = 50, header, variant = 'bordered', children } = props;
|
||||||
const [height, setHeight] = useState(0);
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
const contentRef = useRef(null);
|
const [height, setHeight] = useState(0);
|
||||||
|
const contentRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
setHeight(contentRef?.current?.scrollHeight);
|
setHeight(contentRef?.current?.scrollHeight || 0);
|
||||||
} else {
|
} else {
|
||||||
setHeight(0);
|
setHeight(0);
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
const toggleOpen = () => {
|
const toggleOpen = () => {
|
||||||
setIsOpen(!isOpen);
|
setIsOpen(!isOpen);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const setHeightByContent = () => {
|
||||||
<div
|
setHeight(contentRef?.current?.scrollHeight || 0);
|
||||||
className={classNames('transition-wrapper', {
|
};
|
||||||
bordered: variant === 'bordered',
|
|
||||||
filled: variant === 'filled'
|
useImperativeHandle(ref, () => {
|
||||||
})}
|
return {
|
||||||
>
|
setHeightByContent
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={toggleOpen}
|
className={classNames('transition-wrapper', {
|
||||||
className="header"
|
bordered: variant === 'bordered',
|
||||||
style={{
|
filled: variant === 'filled'
|
||||||
height: minHeight
|
})}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{header}
|
<div
|
||||||
|
onClick={toggleOpen}
|
||||||
|
className="header"
|
||||||
|
style={{
|
||||||
|
// when content is closed, header should have minHeight
|
||||||
|
height: minHeight
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{header}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="content-wrapper"
|
||||||
|
style={{
|
||||||
|
minHeight: isOpen ? height : 0,
|
||||||
|
height: isOpen ? 'auto' : 0
|
||||||
|
}}
|
||||||
|
ref={contentRef}
|
||||||
|
>
|
||||||
|
<div className="content">{children}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
);
|
||||||
className="content-wrapper"
|
}
|
||||||
style={{ height: height }}
|
);
|
||||||
ref={contentRef}
|
|
||||||
>
|
|
||||||
<div className="content">{children}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TransitionWrapper;
|
export default TransitionWrapper;
|
||||||
|
|||||||
@@ -191,44 +191,16 @@ const Models: React.FC = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Column title="IP / PID" dataIndex="address" key="address" />
|
<Column title="IP" dataIndex="address" key="address" />
|
||||||
|
|
||||||
|
<Column title="Temperature" dataIndex="Temperature" key="Temperature" />
|
||||||
|
<Column title="Core" dataIndex="core" key="Core" />
|
||||||
|
<Column title="GPU-Util" dataIndex="gpuUtil" key="gpuUtil" />
|
||||||
|
|
||||||
<Column
|
<Column
|
||||||
title="CPU"
|
title="VRAM"
|
||||||
dataIndex="CPU"
|
|
||||||
key="CPU"
|
|
||||||
render={(text, record: NodeItem) => {
|
|
||||||
return (
|
|
||||||
<RenderProgress record={record} dataIndex="cpu"></RenderProgress>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="Memory"
|
|
||||||
dataIndex="memory"
|
|
||||||
key="Memory"
|
|
||||||
render={(text, record: NodeItem) => {
|
|
||||||
return (
|
|
||||||
<RenderProgress
|
|
||||||
record={record}
|
|
||||||
dataIndex="memory"
|
|
||||||
></RenderProgress>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="GPU"
|
|
||||||
dataIndex="GPU"
|
|
||||||
key="GPU"
|
|
||||||
render={(text, record: NodeItem) => {
|
|
||||||
return (
|
|
||||||
<RenderProgress record={record} dataIndex="gpu"></RenderProgress>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Column
|
|
||||||
title="GRAM"
|
|
||||||
dataIndex="GRAM"
|
dataIndex="GRAM"
|
||||||
key="GRAM"
|
key="VRAM"
|
||||||
render={(text, record: NodeItem) => {
|
render={(text, record: NodeItem) => {
|
||||||
return (
|
return (
|
||||||
<RenderProgress record={record} dataIndex="gram"></RenderProgress>
|
<RenderProgress record={record} dataIndex="gram"></RenderProgress>
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ const Models: React.FC = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Column title="IP / PID" dataIndex="address" key="address" />
|
<Column title="IP" dataIndex="address" key="address" />
|
||||||
<Column
|
<Column
|
||||||
title="CPU"
|
title="CPU"
|
||||||
dataIndex="CPU"
|
dataIndex="CPU"
|
||||||
@@ -226,9 +226,9 @@ const Models: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Column
|
<Column
|
||||||
title="GRAM"
|
title="VRAM"
|
||||||
dataIndex="GRAM"
|
dataIndex="GRAM"
|
||||||
key="GRAM"
|
key="VRAM"
|
||||||
render={(text, record: NodeItem) => {
|
render={(text, record: NodeItem) => {
|
||||||
return (
|
return (
|
||||||
<RenderProgress record={record} dataIndex="gram"></RenderProgress>
|
<RenderProgress record={record} dataIndex="gram"></RenderProgress>
|
||||||
|
|||||||
@@ -32,13 +32,15 @@ const Resources = () => {
|
|||||||
}}
|
}}
|
||||||
extra={[]}
|
extra={[]}
|
||||||
>
|
>
|
||||||
<Tabs
|
<div style={{ marginTop: 70 }}>
|
||||||
type="card"
|
<Tabs
|
||||||
defaultActiveKey="nodes"
|
type="card"
|
||||||
items={items}
|
defaultActiveKey="nodes"
|
||||||
accessKey={activeKey}
|
items={items}
|
||||||
onChange={handleChangeTab}
|
accessKey={activeKey}
|
||||||
></Tabs>
|
onChange={handleChangeTab}
|
||||||
|
></Tabs>
|
||||||
|
</div>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import CopyButton from '@/components/copy-button';
|
import CopyButton from '@/components/copy-button';
|
||||||
import ModalFooter from '@/components/modal-footer';
|
import ModalFooter from '@/components/modal-footer';
|
||||||
import SealInput from '@/components/seal-form/seal-input';
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import { SyncOutlined } from '@ant-design/icons';
|
import { SyncOutlined } from '@ant-design/icons';
|
||||||
import { Form, Modal } from 'antd';
|
import { Form, Modal } from 'antd';
|
||||||
@@ -12,6 +13,12 @@ type AddModalProps = {
|
|||||||
onOk: () => void;
|
onOk: () => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const expirationOptions = [
|
||||||
|
{ label: '1 Month', value: '1m' },
|
||||||
|
{ label: '6 Months', value: '6m' },
|
||||||
|
{ label: 'Never', value: 'never' }
|
||||||
|
];
|
||||||
const AddModal: React.FC<AddModalProps> = ({
|
const AddModal: React.FC<AddModalProps> = ({
|
||||||
title,
|
title,
|
||||||
action,
|
action,
|
||||||
@@ -55,6 +62,13 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
}
|
}
|
||||||
></SealInput.Input>
|
></SealInput.Input>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item name="expiration" rules={[{ required: true }]}>
|
||||||
|
<SealSelect
|
||||||
|
label="Expiration"
|
||||||
|
required
|
||||||
|
options={expirationOptions}
|
||||||
|
></SealSelect>
|
||||||
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import TransitionWrapper from '@/components/transition';
|
|||||||
import { EyeInvisibleOutlined } from '@ant-design/icons';
|
import { EyeInvisibleOutlined } from '@ant-design/icons';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { Button, Input } from 'antd';
|
import { Button, Input } from 'antd';
|
||||||
import { useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import '../style/ground-left.less';
|
import '../style/ground-left.less';
|
||||||
import '../style/system-message-wrap.less';
|
import '../style/system-message-wrap.less';
|
||||||
import ChatFooter from './chat-footer';
|
import ChatFooter from './chat-footer';
|
||||||
@@ -24,9 +24,9 @@ const MessageList: React.FC = () => {
|
|||||||
const [systemMessage, setSystemMessage] = useState('');
|
const [systemMessage, setSystemMessage] = useState('');
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
const [activeIndex, setActiveIndex] = useState(-1);
|
const [activeIndex, setActiveIndex] = useState(-1);
|
||||||
|
const systemRef = useRef<any>(null);
|
||||||
|
|
||||||
const handleSystemMessageChange = (e: any) => {
|
const handleSystemMessageChange = (e: any) => {
|
||||||
console.log('system message:', e.target.value);
|
|
||||||
setSystemMessage(e.target.value);
|
setSystemMessage(e.target.value);
|
||||||
};
|
};
|
||||||
const handleNewMessage = () => {
|
const handleNewMessage = () => {
|
||||||
@@ -74,11 +74,16 @@ const MessageList: React.FC = () => {
|
|||||||
<div className="ground-left">
|
<div className="ground-left">
|
||||||
<PageContainer title={false} className="message-list-wrap">
|
<PageContainer title={false} className="message-list-wrap">
|
||||||
<div style={{ marginBottom: 40 }}>
|
<div style={{ marginBottom: 40 }}>
|
||||||
<TransitionWrapper header={renderLabel()} variant="filled">
|
<TransitionWrapper
|
||||||
|
header={renderLabel()}
|
||||||
|
variant="filled"
|
||||||
|
ref={systemRef}
|
||||||
|
>
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
value={systemMessage}
|
value={systemMessage}
|
||||||
style={{ minHeight: '0px' }}
|
|
||||||
variant="filled"
|
variant="filled"
|
||||||
|
autoSize={true}
|
||||||
|
placeholder="Enter system message here..."
|
||||||
onChange={handleSystemMessageChange}
|
onChange={handleSystemMessageChange}
|
||||||
></Input.TextArea>
|
></Input.TextArea>
|
||||||
</TransitionWrapper>
|
</TransitionWrapper>
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user