feat: paste multiple lines in parameters input
This commit is contained in:
@@ -7,6 +7,7 @@ interface HintInputProps {
|
||||
label?: string;
|
||||
onChange: (value: string) => void;
|
||||
onBlur?: (e: any) => void;
|
||||
onPaste?: (e: any) => void;
|
||||
placeholder?: string;
|
||||
trim?: boolean;
|
||||
sourceOptions?: Global.HintOptions[];
|
||||
@@ -15,7 +16,15 @@ interface HintInputProps {
|
||||
const matchReg = /[^=]+=[^=]*$/;
|
||||
|
||||
const HintInput: React.FC<HintInputProps> = (props) => {
|
||||
const { value, label, onChange, onBlur, sourceOptions, trim = true } = props;
|
||||
const {
|
||||
value,
|
||||
label,
|
||||
onChange,
|
||||
onBlur,
|
||||
onPaste,
|
||||
sourceOptions,
|
||||
trim = true
|
||||
} = props;
|
||||
const cursorPosRef = React.useRef(0);
|
||||
const contextBeforeCursorRef = React.useRef('');
|
||||
const [options, setOptions] = React.useState<
|
||||
@@ -92,6 +101,7 @@ const HintInput: React.FC<HintInputProps> = (props) => {
|
||||
options={options}
|
||||
trim={trim}
|
||||
style={{ flex: 1 }}
|
||||
onPaste={onPaste}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -79,6 +79,41 @@ const ListInput: React.FC<ListInputProps> = (props) => {
|
||||
setList(values);
|
||||
};
|
||||
|
||||
const handleOnPaste = (e: any, index: number) => {
|
||||
const pastedText = e.clipboardData?.getData('text');
|
||||
if (!pastedText) return;
|
||||
|
||||
const lines = pastedText.split(/\r?\n/).filter((line: string) => {
|
||||
const trimmedLine = trim ? line.trim() : line;
|
||||
return trimmedLine.length > 0;
|
||||
});
|
||||
|
||||
if (lines.length <= 1) {
|
||||
// if there's only one line, let the default paste behavior handle it
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const values = _.cloneDeep(list);
|
||||
|
||||
// replace the current item with the first line of the pasted text
|
||||
values[index].value = trim ? lines[0].trim() : lines[0];
|
||||
|
||||
// create new list items for the remaining lines
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
updateCountRef();
|
||||
values.splice(index + i, 0, {
|
||||
value: trim ? lines[i].trim() : lines[i],
|
||||
uid: countRef.current
|
||||
});
|
||||
}
|
||||
|
||||
const valueList = _.map(values, 'value').filter((val: string) => !!val);
|
||||
setList(values);
|
||||
onChange(valueList);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const valueList = _.map(list, 'value').filter((val: string) => !!val);
|
||||
if (!_.isEqual(valueList, dataList)) {
|
||||
@@ -122,6 +157,7 @@ const ListInput: React.FC<ListInputProps> = (props) => {
|
||||
onBlur={(e) => onBlur?.(e, index)}
|
||||
onRemove={() => handleOnRemove(index)}
|
||||
onChange={(val) => handleOnChange(val, index)}
|
||||
onPaste={(e) => handleOnPaste(e, index)}
|
||||
trim={trim}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
|
||||
@@ -8,11 +8,13 @@ interface LabelItemProps {
|
||||
onRemove: () => void;
|
||||
onChange: (value: string) => void;
|
||||
onBlur?: (e: any) => void;
|
||||
onPaste?: (e: any) => void;
|
||||
renderItem?: (
|
||||
data: any,
|
||||
props: {
|
||||
onChange: (value: string) => void;
|
||||
onBlur?: (e: any) => void;
|
||||
onPaste?: (e: any) => void;
|
||||
}
|
||||
) => React.ReactNode;
|
||||
value: string;
|
||||
@@ -29,6 +31,7 @@ const ListItem: React.FC<LabelItemProps> = (props) => {
|
||||
onRemove,
|
||||
onChange,
|
||||
onBlur,
|
||||
onPaste,
|
||||
label,
|
||||
value,
|
||||
options,
|
||||
@@ -47,13 +50,15 @@ const ListItem: React.FC<LabelItemProps> = (props) => {
|
||||
{renderItem ? (
|
||||
renderItem(data, {
|
||||
onChange: handleOnChange,
|
||||
onBlur
|
||||
onBlur,
|
||||
onPaste
|
||||
})
|
||||
) : (
|
||||
<HintInput
|
||||
value={value}
|
||||
onChange={handleOnChange}
|
||||
onBlur={onBlur}
|
||||
onPaste={onPaste}
|
||||
label={label}
|
||||
sourceOptions={options}
|
||||
trim={trim}
|
||||
|
||||
@@ -12,6 +12,7 @@ const Link = Typography.Link;
|
||||
const SealAutoComplete: React.FC<
|
||||
AutoCompleteProps &
|
||||
SealFormItemProps & {
|
||||
onPaste?: (e: any) => void;
|
||||
onInput?: (e: Event) => void;
|
||||
clearSpaceOnBlur?: boolean;
|
||||
}
|
||||
@@ -148,6 +149,7 @@ const SealAutoComplete: React.FC<
|
||||
onChange={handleChange}
|
||||
popupRender={popupRender}
|
||||
onInput={handleOnInput}
|
||||
onPaste={props.onPaste}
|
||||
></AutoComplete>
|
||||
</Wrapper>
|
||||
</SelectWrapper>
|
||||
|
||||
@@ -46,27 +46,19 @@ const ClusterDetailModal = () => {
|
||||
key: 'workers',
|
||||
label: `Workers`,
|
||||
icon: <IconFont type="icon-resources" />,
|
||||
children: (
|
||||
<WorkerList
|
||||
clusterId={id}
|
||||
showAddButton={false}
|
||||
showSelect={false}
|
||||
widths={{ input: 360 }}
|
||||
sourceType="cluster"
|
||||
/>
|
||||
)
|
||||
children: <WorkerList />
|
||||
},
|
||||
{
|
||||
key: 'deployments',
|
||||
label: `Deployments`,
|
||||
icon: <IconFont type="icon-rocket-launch1" />,
|
||||
children: <Deployments clusterId={Number(id)}></Deployments>
|
||||
children: <Deployments></Deployments>
|
||||
},
|
||||
{
|
||||
key: 'gpus',
|
||||
label: `GPUs`,
|
||||
icon: <IconFont type="icon-gpu1" />,
|
||||
children: <GPUList clusterId={Number(id)} widths={{ input: 360 }} />
|
||||
children: <GPUList />
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { tableSorter } from '@/config/settings';
|
||||
import GrafanaIcon from '@/pages/_components/grafana-icon';
|
||||
import { StarFilled } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Tooltip } from 'antd';
|
||||
import { Tooltip, Typography } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMemo } from 'react';
|
||||
@@ -100,9 +100,9 @@ const useClusterColumns = (
|
||||
span: 3,
|
||||
render: (text: string, record: ClusterListItem) => (
|
||||
<>
|
||||
<AutoTooltip ghost title={text}>
|
||||
{text}
|
||||
</AutoTooltip>
|
||||
<Typography.Link onClick={() => onCellClick?.(record, 'name')}>
|
||||
{record.name}
|
||||
</Typography.Link>
|
||||
{record.is_default && (
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
|
||||
@@ -311,7 +311,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
|
||||
}}
|
||||
options={modelFilesSortOptions.current}
|
||||
size="middle"
|
||||
style={{ width: '120px' }}
|
||||
style={{ width: '120px', fontWeight: 400 }}
|
||||
></BaseSelect>
|
||||
</TitleWrapper>
|
||||
{dataSource.loading && (
|
||||
|
||||
@@ -8,7 +8,7 @@ import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import _ from 'lodash';
|
||||
import qs from 'query-string';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
MODELS_API,
|
||||
MODEL_INSTANCE_API,
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import TableList from './components/table-list';
|
||||
import { ListItem } from './config/types';
|
||||
|
||||
const Models: React.FC<{ clusterId?: number }> = ({ clusterId }) => {
|
||||
const Models = () => {
|
||||
const { pagination, setPagination } = usePaginationStatus(
|
||||
PaginationKey.Deployments
|
||||
);
|
||||
@@ -49,7 +49,7 @@ const Models: React.FC<{ clusterId?: number }> = ({ clusterId }) => {
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
search: '',
|
||||
cluster_id: clusterId || 0,
|
||||
cluster_id: 0,
|
||||
categories: [],
|
||||
state: '',
|
||||
sort_by: '',
|
||||
|
||||
@@ -8,15 +8,12 @@ import { useQueryClusterList } from '@/pages/cluster-management/services/use-que
|
||||
import { useIntl, useSearchParams } from '@umijs/max';
|
||||
import { ConfigProvider, Table } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { GPU_DEVICES_API, queryGpuDevicesList } from '../apis';
|
||||
import { GPUDeviceItem } from '../config/types';
|
||||
import useGPUColumns from '../hooks/use-gpu-columns';
|
||||
|
||||
const GPUList: React.FC<{ clusterId?: number; widths?: { input: number } }> = ({
|
||||
clusterId,
|
||||
widths
|
||||
}) => {
|
||||
const GPUList = () => {
|
||||
const {
|
||||
dataSource,
|
||||
queryParams,
|
||||
@@ -31,10 +28,7 @@ const GPUList: React.FC<{ clusterId?: number; widths?: { input: number } }> = ({
|
||||
key: PaginationKey.GPUs,
|
||||
fetchAPI: queryGpuDevicesList,
|
||||
polling: true,
|
||||
API: GPU_DEVICES_API,
|
||||
defaultQueryParams: {
|
||||
cluster_id: clusterId
|
||||
}
|
||||
API: GPU_DEVICES_API
|
||||
});
|
||||
const [searchParams] = useSearchParams();
|
||||
const page = searchParams.get('page');
|
||||
@@ -108,8 +102,7 @@ const GPUList: React.FC<{ clusterId?: number; widths?: { input: number } }> = ({
|
||||
handleInputChange={handleNameChange}
|
||||
handleSelectChange={handleClusterChange}
|
||||
selectOptions={clusterList}
|
||||
showSelect={page !== 'clusters'}
|
||||
widths={{ input: widths?.input || 200 }}
|
||||
showSelect={true}
|
||||
></FilterBar>
|
||||
<ConfigProvider renderEmpty={renderEmpty}>
|
||||
<Table
|
||||
|
||||
@@ -12,7 +12,7 @@ import useGranfanaLink from '@/pages/resources/hooks/use-grafana-link';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { ConfigProvider, Table, message } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
WORKERS_API,
|
||||
deleteWorker,
|
||||
@@ -27,19 +27,7 @@ import UpdateLabels from './update-labels';
|
||||
import WorkerDetailModal from './worker-detail-modal';
|
||||
import WorkerRightActions from './worker-right-actions';
|
||||
|
||||
const Workers: React.FC<{
|
||||
clusterId?: string | number | null;
|
||||
showSelect?: boolean;
|
||||
showAddButton?: boolean;
|
||||
widths?: { input: number };
|
||||
sourceType?: string;
|
||||
}> = ({
|
||||
clusterId,
|
||||
showSelect = true,
|
||||
showAddButton = true,
|
||||
widths = { input: 200 },
|
||||
sourceType = 'resources'
|
||||
}) => {
|
||||
const Workers = () => {
|
||||
const {
|
||||
dataSource,
|
||||
rowSelection,
|
||||
@@ -63,10 +51,7 @@ const Workers: React.FC<{
|
||||
contentForDelete: 'resources.worker',
|
||||
watch: true,
|
||||
API: WORKERS_API,
|
||||
updateManually: true,
|
||||
defaultQueryParams: {
|
||||
cluster_id: clusterId
|
||||
}
|
||||
updateManually: true
|
||||
});
|
||||
const { goToGrafana, ActionButton } = useGranfanaLink({
|
||||
type: 'worker'
|
||||
@@ -255,8 +240,7 @@ const Workers: React.FC<{
|
||||
loadend: dataSource.loadend,
|
||||
firstLoad: extraStatus.firstLoad,
|
||||
sortOrder,
|
||||
handleSelect,
|
||||
sourceType
|
||||
handleSelect
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -268,7 +252,7 @@ const Workers: React.FC<{
|
||||
<>
|
||||
<PageBox>
|
||||
<FilterBar
|
||||
showSelect={showSelect}
|
||||
showSelect={true}
|
||||
selectHolder={intl.formatMessage({ id: 'clusters.filterBy.cluster' })}
|
||||
marginBottom={22}
|
||||
marginTop={30}
|
||||
@@ -280,7 +264,6 @@ const Workers: React.FC<{
|
||||
handleInputChange={handleNameChange}
|
||||
rowSelection={rowSelection}
|
||||
selectOptions={clusterData.list}
|
||||
widths={widths}
|
||||
right={
|
||||
<WorkerRightActions
|
||||
handleDeleteByBatch={handleDeleteBatch}
|
||||
|
||||
@@ -235,7 +235,6 @@ const useWorkerColumns = ({
|
||||
loadend,
|
||||
firstLoad,
|
||||
sortOrder,
|
||||
sourceType,
|
||||
handleSelect
|
||||
}: {
|
||||
clusterData: {
|
||||
@@ -245,7 +244,6 @@ const useWorkerColumns = ({
|
||||
loadend: boolean;
|
||||
firstLoad: boolean;
|
||||
sortOrder: string[];
|
||||
sourceType: string;
|
||||
handleSelect: (action: string, record: ListItem) => void;
|
||||
}): ColumnsType<ListItem> => {
|
||||
const intl = useIntl();
|
||||
@@ -316,7 +314,6 @@ const useWorkerColumns = ({
|
||||
{
|
||||
title: intl.formatMessage({ id: 'clusters.title' }),
|
||||
dataIndex: 'cluster_id',
|
||||
hidden: sourceType === 'cluster',
|
||||
render: (id: number) => (
|
||||
<AutoTooltip ghost maxWidth={240}>
|
||||
{_.get(clusterData.data, id, '')}
|
||||
@@ -440,7 +437,7 @@ const useWorkerColumns = ({
|
||||
)
|
||||
}
|
||||
],
|
||||
[intl, sourceType, sortOrder, clusterData, loadend, firstLoad, handleSelect]
|
||||
[intl, sortOrder, clusterData, loadend, firstLoad, handleSelect]
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user