chore: common components

main
jialin 2 years ago
parent fa522fb89e
commit c948110153

@ -1,4 +1,6 @@
:local(.wrapper) {
@wrapheight: 54px;
@inputheight: 32px;
position: relative;
display: flex;
flex-direction: column;
@ -10,6 +12,31 @@
border-radius: 28px;
padding-inline: 12px;
padding-block: 20px 0;
&.seal-input-wrapper-disabled {
background-color: var(--ant-color-bg-container-disabled);
cursor: not-allowed;
&:hover {
border-color: var(--ant-color-border);
}
}
&.seal-textarea-wrapper {
height: auto;
padding-bottom: 10px;
padding-right: 10px;
}
&.validate-status-error {
border-width: var(--ant-line-width);
border-style: var(--ant-line-type);
border-color: var(--ant-color-error);
&:hover {
border-color: var(--ant-color-error-border-hover);
}
&:focus-within {
border-color: var(--ant-color-error);
}
}
&:hover {
border-color: var(--ant-input-hover-border-color);
transition: all 0.2s ease;
@ -37,13 +64,92 @@
transition: all 0.2s var(--seal-transition-func);
}
}
:global(.ant-input) {
// ==================== select ==================
:global(.ant-select) {
display: flex;
align-items: center;
border: none;
box-shadow: none;
height: @inputheight;
}
:global(.ant-select-selector) {
border: none !important;
padding-block: 5px;
padding-inline: 12px;
box-shadow: none !important;
}
:global(.ant-select-selection-item) {
height: @inputheight;
}
:global(
.ant-select-outlined.ant-select-disabled:not(.ant-select-customize-input)
.ant-select-selector
) {
background-color: transparent;
}
// ==================== input ====================
:global(.ant-input),
:global(.ant-input-password) {
display: flex;
align-items: center;
border: none;
box-shadow: none;
padding-block: 5px;
padding-inline: 12px;
height: @inputheight;
}
:global(.ant-input-number) {
display: flex;
align-items: center;
border: none;
box-shadow: none;
padding: 0;
height: @inputheight;
}
:global(.ant-input-outlined) {
&.seal-textarea {
min-height: 100px !important;
}
}
:global(.ant-input.ant-input-disabled) {
background-color: transparent;
}
:global(input.ant-input-number-input) {
height: @inputheight;
padding-block: 5px;
padding-inline: 12px;
height: 32px;
}
:global(.ant-input-group) {
position: static;
}
:global(.ant-input-group-addon) {
inset-inline-start: unset !important;
border-radius: 0 28px 28px 0;
width: 30px;
background-color: transparent;
}
:global(.ant-input-group-addon:hover) {
background-color: transparent !important;
}
:global(.ant-input-group-wrapper-disabled .ant-input-group-addon) {
background-color: transparent;
}
:global(.ant-input-group-wrapper-disabled .ant-input-group-addon:hover) {
background-color: transparent !important;
}
:global(.ant-input-search-button) {
position: absolute;
top: -20px;
right: -11px;
border-radius: 0 28px 28px 0 !important;
overflow: hidden;
border: none;
height: 52px;
border-left: var(--ant-line-width) var(--ant-line-type)
var(--ant-color-border);
}
:global(.ant-input-number-handler-wrap) {
top: -20px;
height: @wrapheight - 2px;
}
}

@ -4,6 +4,9 @@ interface WrapperProps {
children: React.ReactNode;
label: string;
isFocus: boolean;
status?: string;
className?: string;
disabled?: boolean;
onClick?: () => void;
}
@ -11,10 +14,21 @@ const Wrapper: React.FC<WrapperProps> = ({
children,
label,
isFocus,
status,
className,
disabled,
onClick
}) => {
return (
<div className={wrapperStyle.wrapper} onClick={onClick}>
<div
className={classNames(
wrapperStyle.wrapper,
wrapperStyle[`validate-status-${status}`],
disabled ? wrapperStyle['seal-input-wrapper-disabled'] : '',
className ? wrapperStyle[className] : ''
)}
onClick={onClick}
>
<label
onClick={onClick}
className={classNames(

@ -0,0 +1,68 @@
import type { InputNumberProps } from 'antd';
import { Form, InputNumber } from 'antd';
import { useEffect, useRef, useState } from 'react';
import Wrapper from './components/wrapper';
import { SealFormItemProps } from './types';
const SealInputNumber: React.FC<InputNumberProps & SealFormItemProps> = (
props
) => {
const { label, placeholder, ...rest } = props;
const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null);
const { status } = Form.Item.useStatus();
useEffect(() => {
if (props.value) {
setIsFocus(true);
}
}, [props.value]);
const handleClickWrapper = () => {
if (!props.disabled && !isFocus) {
inputRef.current?.focus?.();
setIsFocus(true);
}
};
const handleChange = (e: any) => {
props.onChange?.(e);
};
const handleOnFocus = (e: any) => {
setIsFocus(true);
props.onFocus?.(e);
};
const handleOnBlur = (e: any) => {
if (!inputRef.current?.value) {
setIsFocus(false);
props.onBlur?.(e);
}
};
const handleInput = (e: any) => {
props.onInput?.(e);
};
return (
<Wrapper
status={status}
label={label || (placeholder as string)}
isFocus={isFocus}
disabled={props.disabled}
onClick={handleClickWrapper}
>
<InputNumber
{...rest}
ref={inputRef}
onFocus={handleOnFocus}
onBlur={handleOnBlur}
onInput={handleInput}
onChange={(e) => handleChange(e)}
></InputNumber>
</Wrapper>
);
};
export default SealInputNumber;

@ -0,0 +1,77 @@
import { Form, Input } from 'antd';
import type { SearchProps } from 'antd/es/input/Search';
import { useEffect, useRef, useState } from 'react';
import Wrapper from './components/wrapper';
import { SealFormItemProps } from './types';
type OnSearch = (
value: string,
event?:
| React.ChangeEvent<HTMLInputElement>
| React.MouseEvent<HTMLElement>
| React.KeyboardEvent<HTMLInputElement>,
info?: {
source?: 'clear' | 'input';
}
) => void;
const SealInputSearch: React.FC<SearchProps & SealFormItemProps> = (props) => {
const { label, placeholder, ...rest } = props;
const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null);
const { status } = Form.Item.useStatus();
useEffect(() => {
if (props.value) {
setIsFocus(true);
}
}, [props.value]);
const handleClickWrapper = () => {
if (!props.disabled && !isFocus) {
inputRef.current?.focus?.();
setIsFocus(true);
}
};
const handleChange = (e: any) => {
props.onChange?.(e);
};
const handleOnFocus = (e: any) => {
setIsFocus(true);
props.onFocus?.(e);
};
const handleOnBlur = (e: any) => {
if (!inputRef.current?.input?.value) {
setIsFocus(false);
props.onBlur?.(e);
}
};
const handleSearch: OnSearch = (...args) => {
props.onSearch?.(...args);
};
return (
<Wrapper
status={status}
label={label || (placeholder as string)}
isFocus={isFocus}
disabled={props.disabled}
onClick={handleClickWrapper}
>
<Input.Search
{...rest}
ref={inputRef}
onFocus={handleOnFocus}
onBlur={handleOnBlur}
onSearch={handleSearch}
onChange={(e) => handleChange(e)}
></Input.Search>
</Wrapper>
);
};
export default SealInputSearch;

@ -0,0 +1,61 @@
import type { InputProps } from 'antd';
import { Form, Input } from 'antd';
import { useEffect, useRef, useState } from 'react';
import Wrapper from './components/wrapper';
import { SealFormItemProps } from './types';
const SealPassword: React.FC<InputProps & SealFormItemProps> = (props) => {
const { label, placeholder, ...rest } = props;
const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null);
const { status } = Form.Item.useStatus();
useEffect(() => {
if (props.value) {
setIsFocus(true);
}
}, [props.value]);
const handleClickWrapper = () => {
if (!props.disabled && !isFocus) {
inputRef.current?.focus?.();
setIsFocus(true);
}
};
const handleChange = (e: any) => {
props.onChange?.(e);
};
const handleOnFocus = (e: any) => {
setIsFocus(true);
props.onFocus?.(e);
};
const handleOnBlur = (e: any) => {
if (!inputRef.current?.input?.value) {
setIsFocus(false);
props.onBlur?.(e);
}
};
return (
<Wrapper
status={status}
label={label || (placeholder as string)}
isFocus={isFocus}
disabled={props.disabled}
onClick={handleClickWrapper}
>
<Input.Password
{...rest}
ref={inputRef}
onFocus={handleOnFocus}
onBlur={handleOnBlur}
onChange={(e) => handleChange(e)}
></Input.Password>
</Wrapper>
);
};
export default SealPassword;

@ -1,18 +1,70 @@
import type { InputProps } from 'antd';
import { Input } from 'antd';
import { Form, Input } from 'antd';
import type { TextAreaProps } from 'antd/es/input/TextArea';
import { useEffect, useRef, useState } from 'react';
import Wrapper from './components/wrapper';
import SealInputNumber from './input-number';
import SealInputSearch from './input-search';
import SealPassword from './password';
import { SealFormItemProps } from './types';
const TextArea: React.FC<InputProps & SealFormItemProps> = (props) => {
console.log('textarea=========', 121);
const SealTextArea: React.FC<TextAreaProps & SealFormItemProps> = (props) => {
const { label, placeholder, style, ...rest } = props;
const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null);
const { status } = Form.Item.useStatus();
useEffect(() => {
if (props.value) {
setIsFocus(true);
}
}, [props.value]);
const handleClickWrapper = () => {
if (!props.disabled && !isFocus) {
inputRef.current?.focus?.();
setIsFocus(true);
}
};
const handleChange = (e: any) => {
props.onChange?.(e);
};
const handleOnFocus = (e: any) => {
setIsFocus(true);
props.onFocus?.(e);
};
const handleOnBlur = (e: any) => {
if (!inputRef.current?.input?.value) {
setIsFocus(false);
props.onBlur?.(e);
}
};
const handleInput = (e: any) => {
props.onInput?.(e);
};
return (
<Wrapper label="input">
<Wrapper
status={status}
label={label || (placeholder as string)}
isFocus={isFocus}
className="seal-textarea-wrapper"
disabled={props.disabled}
onClick={handleClickWrapper}
>
<Input.TextArea
placeholder="send your message"
autoSize={{ minRows: 1, maxRows: 6 }}
size="large"
variant="borderless"
{...rest}
ref={inputRef}
style={{ minHeight: '80px', ...style }}
className="seal-textarea"
onFocus={handleOnFocus}
onBlur={handleOnBlur}
onInput={handleInput}
onChange={(e) => handleChange(e)}
></Input.TextArea>
</Wrapper>
);
@ -22,6 +74,7 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
const { label, placeholder, ...rest } = props;
const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null);
const { status } = Form.Item.useStatus();
useEffect(() => {
if (props.value) {
@ -52,15 +105,22 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
}
};
const handleInput = (e: any) => {
props.onInput?.(e);
};
return (
<Wrapper
status={status}
label={label || (placeholder as string)}
isFocus={isFocus}
disabled={props.disabled}
onClick={handleClickWrapper}
>
<Input
{...rest}
ref={inputRef}
onInput={handleInput}
onFocus={handleOnFocus}
onBlur={handleOnBlur}
onChange={(e) => handleChange(e)}
@ -69,4 +129,10 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
);
};
export default { TextArea, Input: SealInput };
export default {
TextArea: SealTextArea,
Input: SealInput,
Password: SealPassword,
Number: SealInputNumber,
Search: SealInputSearch
} as Record<string, React.FC<InputProps & SealFormItemProps>>;

@ -0,0 +1,71 @@
import { isNotEmptyValue } from '@/utils/index';
import type { SelectProps } from 'antd';
import { Form, Select } from 'antd';
import { useEffect, useRef, useState } from 'react';
import Wrapper from './components/wrapper';
import { SealFormItemProps } from './types';
const SealSelect: React.FC<SelectProps & SealFormItemProps> = (props) => {
const { label, placeholder, children, ...rest } = props;
const [isFocus, setIsFocus] = useState(false);
const inputRef = useRef<any>(null);
const { status } = Form.Item.useStatus();
useEffect(() => {
if (isNotEmptyValue(props.value)) {
setIsFocus(true);
}
}, [props.value]);
const handleClickWrapper = () => {
if (!props.disabled && !isFocus) {
inputRef.current?.focus?.();
setIsFocus(true);
}
};
const handleChange = (val: any, options: any) => {
if (isNotEmptyValue(val)) {
setIsFocus(true);
} else {
setIsFocus(false);
}
props.onChange?.(val, options);
};
const handleOnFocus = (e: any) => {
setIsFocus(true);
props.onFocus?.(e);
};
const handleOnBlur = (e: any) => {
props.onBlur?.(e);
};
const handleOnSearch = (val: any) => {
props.onSearch?.(val);
};
return (
<Wrapper
status={status}
label={label || (placeholder as string)}
isFocus={isFocus}
disabled={props.disabled}
onClick={handleClickWrapper}
>
<Select
{...rest}
ref={inputRef}
onFocus={handleOnFocus}
onBlur={handleOnBlur}
onSearch={handleOnSearch}
onChange={(val, options) => handleChange(val, options)}
>
{children}
</Select>
</Wrapper>
);
};
export default SealSelect;

@ -27,7 +27,10 @@ html {
--ant-font-size-sm: var(--font-size-base);
--ant-font-size-lg: var(--font-size-base);
--ant-font-size-xl: 20px;
--ant-font-size: var(--font-size-base);
--ant-padding-sm: 14px;
--ant-border-radius-lg: 20px;
--ant-color-error: #ff4d4f;
&.ant-popover {
--ant-popover-inner-padding: 26px;
@ -60,10 +63,49 @@ html {
--ant-select-option-font-size: var(--font-size-base);
}
}
body * {
font-weight: var(--font-weight-normal);
}
body {
// input
.ant-input-outlined.ant-input-status-error:not(.ant-input-disabled) {
border: none;
box-shadow: none;
}
.ant-input-outlined.ant-input-status-error:not(.ant-input-disabled):focus,
.ant-input-outlined.ant-input-status-error:not(
.ant-input-disabled
):focus-within {
border: none;
box-shadow: none;
}
// input number
.ant-input-number-outlined.ant-input-number-status-error:not(
.ant-input-number-disabled
) {
border: none;
box-shadow: none;
}
.ant-input-number-outlined.ant-input-number-status-error:not(
.ant-input-number-disabled
):focus,
.ant-input-number-outlined.ant-input-number-status-error:not(
.ant-input-disabled
):focus-within {
border: none;
box-shadow: none;
}
// form item help
.ant-form-item-with-help .ant-form-item-explain {
padding-left: 24px;
}
}
// ======== basic layout style start============
.ant-pro-layout {
height: 100vh;

@ -1,6 +1,7 @@
import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select';
import { INPUT_WIDTH } from '@/constants';
import { Button, Form, Space } from 'antd';
import { Button, Form, Select, Space } from 'antd';
type ParamsSettingsProps = {
seed?: number;
@ -15,7 +16,7 @@ type ParamsSettingsProps = {
maxTokens?: number;
};
const ParamsSettings: React.FC = () => {
const ParamsSettings: React.FC<{ onClose: () => void }> = ({ onClose }) => {
const initialValues = {
seed: 1,
stopSequence: 1,
@ -38,6 +39,11 @@ const ParamsSettings: React.FC = () => {
console.log('handleOnFinishFailed', errorInfo);
};
const handleCancel = () => {
form.resetFields();
onClose();
};
return (
<Form
name="modelparams"
@ -45,91 +51,134 @@ const ParamsSettings: React.FC = () => {
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
>
<Form.Item<ParamsSettingsProps> name="seed" rules={[{ required: true }]}>
<SealInput.Input
label="Seed"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="stopSequence"
rules={[{ required: true }]}
>
<SealInput.Input
label="Stop Sequence"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="temperature"
rules={[{ required: true }]}
>
<SealInput.Input
label="Temperature"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps> name="topK" rules={[{ required: true }]}>
<SealInput.Input
label="TopK"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps> name="topP" rules={[{ required: true }]}>
<SealInput.Input
label="TopP"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="repeatPenalty"
rules={[{ required: true }]}
>
<SealInput.Input
label="Repeat Penalty"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="repeatLastN"
rules={[{ required: true }]}
>
<SealInput.Input
label="Repeat Last N"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps> name="tfsZ" rules={[{ required: true }]}>
<SealInput.Input
label="TFSZ"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="contextLength"
rules={[{ required: true }]}
<div
style={{
maxHeight: '450px',
overflow: 'auto',
paddingRight: 'var(--ant-popover-inner-padding)'
}}
>
<SealInput.Input
label="Context Length"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="maxTokens"
rules={[{ required: true }]}
>
<SealInput.Input
label="Max Tokens"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="seed"
rules={[{ required: true }]}
>
<SealInput.Input
label="Seed"
disabled={true}
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="select"
rules={[{ required: true }]}
>
<SealSelect
label="Select"
disabled={true}
style={{ width: INPUT_WIDTH.default }}
>
<Select.Option value={1}>1</Select.Option>
<Select.Option value={2}>2</Select.Option>
<Select.Option value={3}>3</Select.Option>
</SealSelect>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="stopSequence"
rules={[{ required: true }]}
>
<SealInput.Input
label="Stop Sequence"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="temperature"
rules={[{ required: true }]}
>
<SealInput.Input
label="Temperature"
style={{ width: INPUT_WIDTH.default }}
disabled={true}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="topK"
rules={[{ required: true }]}
>
<SealInput.Input
label="TopK"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="topP"
rules={[{ required: true }]}
>
<SealInput.Input
label="TopP"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="repeatPenalty"
rules={[{ required: true }]}
>
<SealInput.Input
label="Repeat Penalty"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="repeatLastN"
rules={[{ required: true }]}
>
<SealInput.Search
label="Repeat Last N"
style={{ width: INPUT_WIDTH.default }}
disabled={true}
></SealInput.Search>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="tfsZ"
rules={[{ required: true }]}
>
<SealInput.Input
label="TFSZ"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Input>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="contextLength"
rules={[{ required: true }]}
>
<SealInput.Number
label="Context Length"
style={{ width: INPUT_WIDTH.default }}
></SealInput.Number>
</Form.Item>
<Form.Item<ParamsSettingsProps>
name="maxTokens"
rules={[{ required: true }]}
>
<SealInput.TextArea
label="Max Tokens"
style={{ width: INPUT_WIDTH.default }}
></SealInput.TextArea>
</Form.Item>
</div>
<Form.Item noStyle>
<Space
size={20}
align="end"
style={{ width: '100%', display: 'flex', justifyContent: 'end' }}
style={{
width: '100%',
display: 'flex',
justifyContent: 'end',
paddingRight: 'var(--ant-popover-inner-padding)',
paddingTop: 'var(--ant-popover-inner-padding)'
}}
>
<Button>Cancel</Button>
<Button onClick={handleCancel}>Cancel</Button>
<Button type="primary" htmlType="submit">
Submit
</Button>

@ -1,7 +1,6 @@
import SealPopover from '@/components/popover';
import { ControlOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import { Button, Select, Space } from 'antd';
import { Button, Popover, Select, Space } from 'antd';
import { useEffect, useState } from 'react';
import ChatContent from './components/chatContent';
import MessageInput from './components/messageInput';
@ -17,6 +16,7 @@ const Playground: React.FC = () => {
const [ModelList, setModelList] = useState(dataList);
const [messageList, setMessageList] = useState<any[]>([]);
const [selectedModel, setSelectedModel] = useState('llama3:latest');
const [showPopover, setShowPopover] = useState(false);
const handleSelectChange = (value: string) => {
setSelectedModel(value);
@ -28,6 +28,14 @@ const Playground: React.FC = () => {
setMessageList(['1']);
};
const handleTogglePopover = () => {
setShowPopover(!showPopover);
};
const handleClosePopover = () => {
setShowPopover(false);
};
useEffect(() => {
getMessageList();
}, [selectedModel]);
@ -45,21 +53,26 @@ const Playground: React.FC = () => {
onChange={handleSelectChange}
variant="filled"
></Select>
<SealPopover
content={<ParamsSettings></ParamsSettings>}
<Popover
content={
<ParamsSettings onClose={handleClosePopover}></ParamsSettings>
}
destroyTooltipOnHide={true}
title="Params Settings"
trigger="click"
arrow={false}
overlayInnerStyle={{ maxHeight: '500px', overflow: 'auto' }}
open={showPopover}
overlayStyle={{ top: 70 }}
overlayInnerStyle={{ paddingRight: 0 }}
placement="bottomRight"
>
<Button
onClick={handleTogglePopover}
type="primary"
shape="circle"
icon={<ControlOutlined />}
></Button>
</SealPopover>
</Popover>
</Space>
]}
footer={[<MessageInput />]}

@ -0,0 +1,6 @@
export const isNotEmptyValue = (value: any) => {
if (Array.isArray(value)) {
return value.length > 0;
}
return value !== null && value !== undefined && value !== '';
};
Loading…
Cancel
Save