refactor: add new editable text component which allow to change group title

This commit is contained in:
moonrailgun 2024-09-16 21:50:22 +08:00
parent 946ecaf9f9
commit e323e104e0
3 changed files with 72 additions and 36 deletions

View File

@ -1,38 +1,46 @@
import { Input } from 'antd';
import clsx from 'clsx';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useWatch } from '../hooks/useWatch'; import { useWatch } from '../hooks/useWatch';
import { Input } from './ui/input';
import { useEvent } from '@/hooks/useEvent';
import { cn } from '@/utils/style';
interface EditableTextProps { interface EditableTextProps {
className?: string; className?: string;
enable?: boolean;
defaultValue: string; defaultValue: string;
onSave: (text: string) => void; onSave: (text: string) => void;
} }
export const EditableText: React.FC<EditableTextProps> = React.memo((props) => { export const EditableText: React.FC<EditableTextProps> = React.memo((props) => {
const [text, setText] = useState(props.defaultValue); const [text, setText] = useState(props.defaultValue);
const enable = props.enable ?? true; const [editing, setEditing] = useState(false);
const inputRef = React.useRef<HTMLInputElement>(null);
useWatch([props.defaultValue], () => { useWatch([props.defaultValue], () => {
setText(props.defaultValue); setText(props.defaultValue);
}); });
const handleClick = useEvent(() => {
setEditing(true);
});
return ( return (
<> <div className={cn('cursor-text', props.className)}>
{enable ? ( {editing ? (
<Input <Input
className={clsx( ref={inputRef}
props.className, autoFocus={true}
'rounded-none border-0 p-0 !shadow-none outline-0' type="text"
)} className="h-[1.5em] border-none p-0 text-base shadow-none focus-visible:ring-0"
value={text} value={text}
onChange={(e) => setText(e.target.value)} onChange={(e) => setText(e.target.value)}
onBlur={(e) => props.onSave(e.target.value)} onBlur={() => {
setEditing(false);
props.onSave(text);
}}
/> />
) : ( ) : (
<span className={props.className}>{text}</span> <span onClick={handleClick}>{text}</span>
)} )}
</> </div>
); );
}); });
EditableText.displayName = 'EditableText'; EditableText.displayName = 'EditableText';

View File

@ -12,6 +12,7 @@ import { Separator } from '@/components/ui/separator';
import { MonitorPicker } from '../MonitorPicker'; import { MonitorPicker } from '../MonitorPicker';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
import { set } from 'lodash-es'; import { set } from 'lodash-es';
import { EditableText } from '@/components/EditableText';
export const leafItemSchema = z.object({ export const leafItemSchema = z.object({
key: z.string(), key: z.string(),
@ -90,6 +91,24 @@ export const MonitorStatusPageServiceList: React.FC<MonitorStatusPageServiceList
props.onChange(newList); props.onChange(newList);
}); });
const handleChangeGroupTitle = useEvent(
(groupKey: string, title: string) => {
const index = props.value.findIndex((item) => item.key === groupKey);
if (index === -1) {
return;
}
const newList = [...props.value];
if (!('children' in newList[index])) {
return;
}
newList[index].title = title;
props.onChange(newList);
}
);
const handleDeleteItem = useEvent((groupKey: string, itemKey: string) => { const handleDeleteItem = useEvent((groupKey: string, itemKey: string) => {
const newList = [...props.value]; const newList = [...props.value];
const groupIndex = newList.findIndex((item) => item.key === groupKey); const groupIndex = newList.findIndex((item) => item.key === groupKey);
@ -145,31 +164,34 @@ export const MonitorStatusPageServiceList: React.FC<MonitorStatusPageServiceList
onChange={props.onChange} onChange={props.onChange}
renderGroup={(group, children, level) => ( renderGroup={(group, children, level) => (
<div> <div>
<div className={cn('flex items-center gap-2')}> {level > 0 && (
<span>{group.title}</span> <div className={cn('flex items-center gap-2')}>
<EditableText
className="flex-1 overflow-hidden text-ellipsis text-nowrap"
defaultValue={group.title}
onSave={(text) => handleChangeGroupTitle(group.key, text)}
/>
{level > 0 && ( <Button
<> className="h-6 w-6"
<Button variant="outline"
className="h-6 w-6" size="icon"
variant="outline" type="button"
size="icon" Icon={LuPlusCircle}
type="button" onClick={() => handleAddItem(group.key)}
Icon={LuPlusCircle} />
onClick={() => handleAddItem(group.key)}
/> <Button
className="h-6 w-6"
variant="outline"
size="icon"
type="button"
Icon={LuTrash}
onClick={() => handleDeleteGroup(group.key)}
/>
</div>
)}
<Button
className="h-6 w-6"
variant="outline"
size="icon"
type="button"
Icon={LuTrash}
onClick={() => handleDeleteGroup(group.key)}
/>
</>
)}
</div>
<div <div
className={cn(level > 0 && 'border-l-4 border-gray-600 p-2')} className={cn(level > 0 && 'border-l-4 border-gray-600 p-2')}
> >

View File

@ -5,6 +5,7 @@ import {
MonitorStatusPageServiceList, MonitorStatusPageServiceList,
} from '@/components/monitor/StatusPage/ServiceList'; } from '@/components/monitor/StatusPage/ServiceList';
import { useState } from 'react'; import { useState } from 'react';
import { EditableText } from '@/components/EditableText';
export const Route = createFileRoute('/playground')({ export const Route = createFileRoute('/playground')({
beforeLoad: () => { beforeLoad: () => {
@ -45,6 +46,11 @@ function PageComponent() {
return ( return (
<div> <div>
<EditableText
defaultValue="fooooooooo"
onSave={() => console.log('save')}
/>
<MonitorStatusPageServiceList value={list} onChange={setList} /> <MonitorStatusPageServiceList value={list} onChange={setList} />
</div> </div>
); );