feat: dashboard card title allow edit
This commit is contained in:
parent
90f22afe28
commit
d03f60f6a1
38
src/client/components/EditableText.tsx
Normal file
38
src/client/components/EditableText.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Input } from 'antd';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useWatch } from '../hooks/useWatch';
|
||||||
|
|
||||||
|
interface EditableTextProps {
|
||||||
|
className?: string;
|
||||||
|
enable?: boolean;
|
||||||
|
defaultValue: string;
|
||||||
|
onSave: (text: string) => void;
|
||||||
|
}
|
||||||
|
export const EditableText: React.FC<EditableTextProps> = React.memo((props) => {
|
||||||
|
const [text, setText] = useState(props.defaultValue);
|
||||||
|
const enable = props.enable ?? true;
|
||||||
|
|
||||||
|
useWatch([props.defaultValue], () => {
|
||||||
|
setText(props.defaultValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{enable ? (
|
||||||
|
<Input
|
||||||
|
className={clsx(
|
||||||
|
props.className,
|
||||||
|
'border-0 p-0 outline-0 !shadow-none rounded-none'
|
||||||
|
)}
|
||||||
|
value={text}
|
||||||
|
onChange={(e) => setText(e.target.value)}
|
||||||
|
onBlur={(e) => props.onSave(e.target.value)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className={props.className}>{text}</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
EditableText.displayName = 'EditableText';
|
@ -23,6 +23,7 @@ export const DashboardGrid: React.FC<DashboardGridProps> = React.memo(
|
|||||||
className={clsx('layout', isEditMode && 'select-none')}
|
className={clsx('layout', isEditMode && 'select-none')}
|
||||||
layouts={layouts}
|
layouts={layouts}
|
||||||
rowHeight={50}
|
rowHeight={50}
|
||||||
|
draggableCancel=".non-draggable"
|
||||||
isDraggable={isEditMode}
|
isDraggable={isEditMode}
|
||||||
isResizable={isEditMode}
|
isResizable={isEditMode}
|
||||||
breakpoints={{ lg: 1200, md: 768, sm: 0 }}
|
breakpoints={{ lg: 1200, md: 768, sm: 0 }}
|
||||||
|
@ -2,7 +2,7 @@ import { useMemo } from 'react';
|
|||||||
import { DashboardItem, useDashboardStore } from '../../../store/dashboard';
|
import { DashboardItem, useDashboardStore } from '../../../store/dashboard';
|
||||||
import { WebsiteOverviewItem } from './WebsiteOverviewItem';
|
import { WebsiteOverviewItem } from './WebsiteOverviewItem';
|
||||||
import { NotFoundTip } from '../../NotFoundTip';
|
import { NotFoundTip } from '../../NotFoundTip';
|
||||||
import { Button, Card } from 'antd';
|
import { Button, Card, Input, Typography } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DeleteOutlined } from '@ant-design/icons';
|
import { DeleteOutlined } from '@ant-design/icons';
|
||||||
import { useEvent } from '../../../hooks/useEvent';
|
import { useEvent } from '../../../hooks/useEvent';
|
||||||
@ -11,13 +11,14 @@ import { MonitorHealthBarItem } from './MonitorHealthBarItem';
|
|||||||
import { MonitorMetricsItem } from './MonitorMetricsItem';
|
import { MonitorMetricsItem } from './MonitorMetricsItem';
|
||||||
import { MonitorChartItem } from './MonitorChartItem';
|
import { MonitorChartItem } from './MonitorChartItem';
|
||||||
import { MonitorEventsItem } from './MonitorEventsItem';
|
import { MonitorEventsItem } from './MonitorEventsItem';
|
||||||
|
import { EditableText } from '../../EditableText';
|
||||||
|
|
||||||
interface DashboardGridItemProps {
|
interface DashboardGridItemProps {
|
||||||
item: DashboardItem;
|
item: DashboardItem;
|
||||||
}
|
}
|
||||||
export const DashboardGridItem: React.FC<DashboardGridItemProps> = React.memo(
|
export const DashboardGridItem: React.FC<DashboardGridItemProps> = React.memo(
|
||||||
(props) => {
|
(props) => {
|
||||||
const { isEditMode, removeItem } = useDashboardStore();
|
const { isEditMode, removeItem, changeItemTitle } = useDashboardStore();
|
||||||
const { key, id, title, type } = props.item;
|
const { key, id, title, type } = props.item;
|
||||||
|
|
||||||
const inner = useMemo(() => {
|
const inner = useMemo(() => {
|
||||||
@ -40,7 +41,6 @@ export const DashboardGridItem: React.FC<DashboardGridItemProps> = React.memo(
|
|||||||
|
|
||||||
const handleDelete = useEvent(
|
const handleDelete = useEvent(
|
||||||
(e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
(e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||||
console.log('e', e, key);
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
removeItem(key);
|
removeItem(key);
|
||||||
}
|
}
|
||||||
@ -49,7 +49,14 @@ export const DashboardGridItem: React.FC<DashboardGridItemProps> = React.memo(
|
|||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className="h-full w-full overflow-auto"
|
className="h-full w-full overflow-auto"
|
||||||
title={title}
|
title={
|
||||||
|
<EditableText
|
||||||
|
className="non-draggable"
|
||||||
|
enable={isEditMode}
|
||||||
|
defaultValue={title}
|
||||||
|
onSave={(text) => changeItemTitle(key, text)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
headStyle={{ padding: 10, minHeight: 40 }}
|
headStyle={{ padding: 10, minHeight: 40 }}
|
||||||
bodyStyle={{ padding: 10 }}
|
bodyStyle={{ padding: 10 }}
|
||||||
extra={
|
extra={
|
||||||
|
@ -2,7 +2,7 @@ import { DependencyList, useLayoutEffect } from 'react';
|
|||||||
import { useEvent } from './useEvent';
|
import { useEvent } from './useEvent';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听变更并触发回调
|
* Listen for changes and trigger callbacks
|
||||||
*/
|
*/
|
||||||
export function useWatch(deps: DependencyList, cb: () => void) {
|
export function useWatch(deps: DependencyList, cb: () => void) {
|
||||||
const memoizedFn = useEvent(cb);
|
const memoizedFn = useEvent(cb);
|
||||||
|
@ -25,6 +25,7 @@ interface DashboardState {
|
|||||||
items: DashboardItem[];
|
items: DashboardItem[];
|
||||||
addItem: (type: DashboardItemType, id: string, title: string) => void;
|
addItem: (type: DashboardItemType, id: string, title: string) => void;
|
||||||
removeItem: (key: string) => void;
|
removeItem: (key: string) => void;
|
||||||
|
changeItemTitle: (key: string, title: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultBlankLayouts = {
|
export const defaultBlankLayouts = {
|
||||||
@ -69,6 +70,22 @@ export const useDashboardStore = create<DashboardState>((set, get) => ({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
changeItemTitle: (key: string, title: string) => {
|
||||||
|
set((state) => {
|
||||||
|
return {
|
||||||
|
items: state.items.map((item) => {
|
||||||
|
if (item.key === key) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
title,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const defaultItemLayout: Record<DashboardItemType, Omit<Layout, 'i'>> = {
|
export const defaultItemLayout: Record<DashboardItemType, Omit<Layout, 'i'>> = {
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
TitleContentToken,
|
TitleContentToken,
|
||||||
} from './type';
|
} from './type';
|
||||||
|
|
||||||
export { ContentToken };
|
export type { ContentToken };
|
||||||
|
|
||||||
export const token = {
|
export const token = {
|
||||||
text: (content: string): TextContentToken => ({
|
text: (content: string): TextContentToken => ({
|
||||||
|
Loading…
Reference in New Issue
Block a user