mirror of https://github.com/msgbyte/tailchat
refactor: fullmodalfield and group summary
parent
e585d1c044
commit
64c3402bd7
@ -0,0 +1,141 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import _isString from 'lodash/isString';
|
||||
import _isNil from 'lodash/isNil';
|
||||
import { Button, Input } from 'antd';
|
||||
import { Icon } from '@iconify/react';
|
||||
|
||||
export type FullModalFieldEditorRenderComponent = React.FC<{
|
||||
value: string;
|
||||
onChange: (val: string) => void;
|
||||
}>;
|
||||
|
||||
interface FullModalFieldProps {
|
||||
/**
|
||||
* 字段标题
|
||||
*/
|
||||
title: React.ReactNode;
|
||||
|
||||
/**
|
||||
* 字段内容
|
||||
* 如果没有则向下取value的值
|
||||
*/
|
||||
content?: React.ReactNode;
|
||||
|
||||
/**
|
||||
* 是否可编辑
|
||||
*/
|
||||
editable?: boolean;
|
||||
|
||||
/**
|
||||
* 如果可编辑则必填
|
||||
* 用于告知组件当前的值
|
||||
*/
|
||||
value?: string;
|
||||
|
||||
/**
|
||||
* 渲染编辑视图的编辑器
|
||||
*/
|
||||
renderEditor?: FullModalFieldEditorRenderComponent;
|
||||
|
||||
/**
|
||||
* 编辑完成后的回调
|
||||
*/
|
||||
onSave?: (val: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段编辑器
|
||||
*/
|
||||
const FullModalFieldEditor: React.FC<FullModalFieldProps> = React.memo(
|
||||
(props) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [editingValue, setEditingValue] = useState(props.value ?? '');
|
||||
|
||||
useEffect(() => {
|
||||
setEditingValue(props.value ?? '');
|
||||
}, [props.value]);
|
||||
|
||||
const handleEditing = useCallback(() => {
|
||||
setIsEditing(!isEditing);
|
||||
}, [isEditing]);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
props.onSave?.(editingValue);
|
||||
setIsEditing(false);
|
||||
}, [props.onSave, editingValue]);
|
||||
|
||||
const EditorComponent = props.renderEditor;
|
||||
|
||||
return (
|
||||
<div className="flex w-full items-end">
|
||||
{isEditing && !_isNil(EditorComponent) ? (
|
||||
<EditorComponent value={editingValue} onChange={setEditingValue} />
|
||||
) : (
|
||||
<span>{props.content ?? props.value}</span>
|
||||
)}
|
||||
|
||||
<Button
|
||||
icon={
|
||||
isEditing ? (
|
||||
<Icon className="anticon" icon="mdi:close" />
|
||||
) : (
|
||||
<Icon className="anticon" icon="mdi:square-edit-outline" />
|
||||
)
|
||||
}
|
||||
onClick={handleEditing}
|
||||
/>
|
||||
|
||||
{isEditing && (
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<Icon className="anticon" icon="mdi:check" />}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
FullModalFieldEditor.displayName = 'FullModalFieldEditor';
|
||||
|
||||
export const FullModalField: React.FC<FullModalFieldProps> = React.memo(
|
||||
(props) => {
|
||||
const valueTitle = _isString(props.value) ? props.value : undefined;
|
||||
|
||||
const allowEditor = props.editable === true && !_isNil(props.renderEditor);
|
||||
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<div className="text-xs text-gray-400 mb-2">{props.title}</div>
|
||||
<div className="h-10 text-base truncate" title={valueTitle}>
|
||||
{allowEditor === true ? (
|
||||
<FullModalFieldEditor {...props} />
|
||||
) : (
|
||||
<span>{props.content ?? props.value}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
FullModalField.displayName = 'FullModalField';
|
||||
|
||||
/**
|
||||
* 默认的输入框字段编辑器
|
||||
*/
|
||||
export const DefaultFullModalInputEditorRender: FullModalFieldEditorRenderComponent =
|
||||
({ value, onChange }) => (
|
||||
<Input value={value} onChange={(e) => onChange(e.target.value)} />
|
||||
);
|
||||
|
||||
/**
|
||||
* 默认的多行输入框字段编辑器
|
||||
*/
|
||||
export const DefaultFullModalTextAreaEditorRender: FullModalFieldEditorRenderComponent =
|
||||
({ value, onChange }) => (
|
||||
<Input.TextArea
|
||||
autoSize={true}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
);
|
@ -1,8 +1,49 @@
|
||||
import { Avatar } from '@/components/Avatar';
|
||||
import {
|
||||
DefaultFullModalInputEditorRender,
|
||||
FullModalField,
|
||||
} from '@/components/FullModalField';
|
||||
import { NoData } from '@/components/NoData';
|
||||
import React from 'react';
|
||||
import {
|
||||
modifyGroupField,
|
||||
t,
|
||||
useAsyncRequest,
|
||||
useGroupInfo,
|
||||
} from 'tailchat-shared';
|
||||
|
||||
export const GroupSummary: React.FC<{
|
||||
groupId: string;
|
||||
}> = React.memo((props) => {
|
||||
return <div>GroupSummary: {props.groupId}</div>;
|
||||
}> = React.memo(({ groupId }) => {
|
||||
const groupInfo = useGroupInfo(groupId);
|
||||
|
||||
const [, handleUpdateGroupName] = useAsyncRequest(
|
||||
async (newName: string) => {
|
||||
await modifyGroupField(groupId, 'name', newName);
|
||||
},
|
||||
[groupId]
|
||||
);
|
||||
|
||||
if (!groupInfo) {
|
||||
return <NoData message={t('无法获取到群组信息')} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<div className="w-1/2">
|
||||
<Avatar size={128} name={groupInfo.name} src={groupInfo.avatar} />
|
||||
</div>
|
||||
|
||||
<div className="w-1/2">
|
||||
<FullModalField
|
||||
title={t('群组名')}
|
||||
value={groupInfo.name}
|
||||
editable={true}
|
||||
renderEditor={DefaultFullModalInputEditorRender}
|
||||
onSave={handleUpdateGroupName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
GroupSummary.displayName = 'GroupSummary';
|
||||
|
Loading…
Reference in New Issue