feat: refactor sortable group component and add edit body component
This commit is contained in:
parent
72a1e7b024
commit
946ecaf9f9
34
src/client/components/DeprecatedBadge.tsx
Normal file
34
src/client/components/DeprecatedBadge.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
|
||||
interface DeprecatedBadgeProps {
|
||||
tip?: string;
|
||||
}
|
||||
|
||||
export const DeprecatedBadge: React.FC<DeprecatedBadgeProps> = React.memo(
|
||||
(props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const el = (
|
||||
<Badge className="mx-1 px-1 py-0.5 text-xs" variant="secondary">
|
||||
{t('Deprecated')}
|
||||
</Badge>
|
||||
);
|
||||
|
||||
if (!props.tip) {
|
||||
return el;
|
||||
} else {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>{el}</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{props.tip}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
DeprecatedBadge.displayName = 'DeprecatedBadge';
|
@ -2,15 +2,15 @@ import React from 'react';
|
||||
import { DragDropContext, DropResult } from 'react-beautiful-dnd';
|
||||
import { useEvent } from '@/hooks/useEvent';
|
||||
import { reorder } from '@/utils/reorder';
|
||||
import { BaseSortableData } from './types';
|
||||
import { SortableItem } from './types';
|
||||
|
||||
interface SortableContextProps<T extends BaseSortableData = BaseSortableData> {
|
||||
interface SortableContextProps<T extends SortableItem> {
|
||||
list: T[];
|
||||
onChange: (list: T[]) => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const SortableContext = <T extends BaseSortableData>(
|
||||
export const SortableContext = <T extends SortableItem>(
|
||||
props: SortableContextProps<T>
|
||||
) => {
|
||||
const { list, onChange, children } = props;
|
||||
@ -37,50 +37,50 @@ export const SortableContext = <T extends BaseSortableData>(
|
||||
|
||||
const final = [...list];
|
||||
const sourceGroupIndex = final.findIndex(
|
||||
(group) => group.id === result.source.droppableId
|
||||
(group) => group.key === result.source.droppableId
|
||||
);
|
||||
if (sourceGroupIndex === -1) {
|
||||
return;
|
||||
}
|
||||
const destinationGroupIndex = final.findIndex(
|
||||
(group) => group.id === result.destination?.droppableId
|
||||
(group) => group.key === result.destination?.droppableId
|
||||
);
|
||||
if (destinationGroupIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sourceGroupIndex === destinationGroupIndex) {
|
||||
if (!('items' in final[sourceGroupIndex])) {
|
||||
if (!('children' in final[sourceGroupIndex])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// same group
|
||||
final[sourceGroupIndex].items = reorder(
|
||||
final[sourceGroupIndex].items!,
|
||||
final[sourceGroupIndex].children = reorder(
|
||||
final[sourceGroupIndex].children!,
|
||||
result.source.index,
|
||||
result.destination.index
|
||||
);
|
||||
} else {
|
||||
// cross group
|
||||
if (
|
||||
!('items' in final[sourceGroupIndex]) ||
|
||||
!('items' in final[destinationGroupIndex])
|
||||
!('children' in final[sourceGroupIndex]) ||
|
||||
!('children' in final[destinationGroupIndex])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceGroupItems = Array.from(
|
||||
final[sourceGroupIndex].items ?? []
|
||||
final[sourceGroupIndex].children ?? []
|
||||
);
|
||||
const [removed] = sourceGroupItems.splice(result.source.index, 1);
|
||||
|
||||
const destinationGroupItems = Array.from(
|
||||
final[destinationGroupIndex].items ?? []
|
||||
final[destinationGroupIndex].children ?? []
|
||||
);
|
||||
destinationGroupItems.splice(result.destination.index, 0, removed);
|
||||
|
||||
final[sourceGroupIndex].items = sourceGroupItems;
|
||||
final[destinationGroupIndex].items = destinationGroupItems;
|
||||
final[sourceGroupIndex].children = sourceGroupItems;
|
||||
final[destinationGroupIndex].children = destinationGroupItems;
|
||||
}
|
||||
|
||||
onChange(final);
|
||||
|
@ -3,58 +3,70 @@ import { SortableContext } from './SortableContext';
|
||||
import { StrictModeDroppable } from './StrictModeDroppable';
|
||||
import { useEvent } from '@/hooks/useEvent';
|
||||
import { Draggable } from 'react-beautiful-dnd';
|
||||
import { BaseSortableData, ExtractGroup, ExtractItem } from './types';
|
||||
import { SortableGroupItem, SortableItem, SortableLeafItem } from './types';
|
||||
|
||||
interface SortableGroupProps<T extends BaseSortableData> {
|
||||
list: T[];
|
||||
onChange: (list: T[]) => void;
|
||||
interface SortableGroupProps<GroupProps, ItemProps> {
|
||||
list: SortableItem<GroupProps, ItemProps>[];
|
||||
onChange: (list: SortableItem<GroupProps, ItemProps>[]) => void;
|
||||
renderGroup: (
|
||||
group: ExtractGroup<T>,
|
||||
children: React.ReactNode
|
||||
group: SortableGroupItem<GroupProps, ItemProps>,
|
||||
children: React.ReactNode,
|
||||
level: number
|
||||
) => React.ReactNode;
|
||||
renderItem: (
|
||||
item: SortableLeafItem<ItemProps>,
|
||||
index: number,
|
||||
group: SortableGroupItem<GroupProps, ItemProps>
|
||||
) => React.ReactNode;
|
||||
renderItem: (item: ExtractItem<T>) => React.ReactNode;
|
||||
}
|
||||
|
||||
export const SortableGroup = <T extends BaseSortableData>(
|
||||
props: SortableGroupProps<T>
|
||||
export const SortableGroup = <GroupProps, ItemProps>(
|
||||
props: SortableGroupProps<GroupProps, ItemProps>
|
||||
) => {
|
||||
const { list, onChange, renderGroup, renderItem } = props;
|
||||
|
||||
const renderItemEl = useEvent((item: ExtractItem<T>, index: number) => {
|
||||
const renderItemEl = useEvent(
|
||||
(
|
||||
item: SortableLeafItem<ItemProps>,
|
||||
index: number,
|
||||
group: SortableGroupItem<GroupProps, ItemProps>
|
||||
) => {
|
||||
return (
|
||||
<Draggable key={item.id} draggableId={item.id} index={index}>
|
||||
<Draggable key={item.key} draggableId={item.key} index={index}>
|
||||
{(dragProvided) => (
|
||||
<div
|
||||
ref={dragProvided.innerRef}
|
||||
{...dragProvided.draggableProps}
|
||||
{...dragProvided.dragHandleProps}
|
||||
>
|
||||
{renderItem(item)}
|
||||
{renderItem(item, index, group)}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const renderGroupEl = useEvent((group: ExtractGroup<T>, level = 0) => {
|
||||
const renderGroupEl = useEvent(
|
||||
(group: SortableGroupItem<GroupProps, ItemProps>, level = 0) => {
|
||||
return (
|
||||
<StrictModeDroppable
|
||||
droppableId={group.id}
|
||||
type={group.type}
|
||||
key={group.id}
|
||||
droppableId={group.key}
|
||||
type={level === 0 ? 'root' : 'group'}
|
||||
key={group.key}
|
||||
>
|
||||
{(dropProvided) => (
|
||||
<div ref={dropProvided.innerRef} {...dropProvided.droppableProps}>
|
||||
{renderGroup(
|
||||
group,
|
||||
<>
|
||||
{group.items.map((item, index) =>
|
||||
item.type === 'item' ? (
|
||||
renderItemEl(item as ExtractItem<T>, index)
|
||||
{group.children.map((item, index) =>
|
||||
!('children' in item) ? (
|
||||
renderItemEl(item, index, group)
|
||||
) : (
|
||||
<Draggable
|
||||
draggableId={item.id}
|
||||
key={item.id}
|
||||
draggableId={item.key}
|
||||
key={item.key}
|
||||
index={index}
|
||||
>
|
||||
{(dragProvided) => (
|
||||
@ -63,13 +75,14 @@ export const SortableGroup = <T extends BaseSortableData>(
|
||||
{...dragProvided.draggableProps}
|
||||
{...dragProvided.dragHandleProps}
|
||||
>
|
||||
{renderGroupEl(item as ExtractGroup<T>, level + 1)}
|
||||
{renderGroupEl(item, level + 1)}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
</>,
|
||||
level
|
||||
)}
|
||||
|
||||
{dropProvided.placeholder}
|
||||
@ -77,16 +90,19 @@ export const SortableGroup = <T extends BaseSortableData>(
|
||||
)}
|
||||
</StrictModeDroppable>
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<SortableContext<T> list={list} onChange={onChange}>
|
||||
<SortableContext<SortableItem<GroupProps, ItemProps>>
|
||||
list={list}
|
||||
onChange={onChange}
|
||||
>
|
||||
{renderGroupEl(
|
||||
{
|
||||
id: 'root',
|
||||
type: 'root' as const,
|
||||
items: list,
|
||||
} as any,
|
||||
key: 'root',
|
||||
children: list,
|
||||
} as SortableGroupItem<GroupProps, ItemProps>,
|
||||
0
|
||||
)}
|
||||
</SortableContext>
|
||||
|
@ -1,33 +1,14 @@
|
||||
import { Id } from 'react-beautiful-dnd';
|
||||
|
||||
export type BaseSortableItem = {
|
||||
type: 'item';
|
||||
id: Id;
|
||||
};
|
||||
export type SortableItem<GroupProps = unknown, ItemProps = unknown> =
|
||||
| SortableLeafItem<ItemProps>
|
||||
| SortableGroupItem<GroupProps, ItemProps>;
|
||||
|
||||
export type BaseSortableGroup<GroupProps = unknown, ItemProps = unknown> = {
|
||||
type: 'group';
|
||||
id: Id;
|
||||
title?: string;
|
||||
items: ((BaseSortableGroup & GroupProps) | (BaseSortableItem & ItemProps))[];
|
||||
};
|
||||
export type SortableGroupItem<GroupProps = unknown, ItemProps = unknown> = {
|
||||
key: Id;
|
||||
children: SortableItem<GroupProps, ItemProps>[];
|
||||
} & GroupProps;
|
||||
|
||||
export type BaseSortableRoot<GroupProps = unknown> = {
|
||||
type: 'root';
|
||||
id: Id;
|
||||
title?: string;
|
||||
items: (BaseSortableItem & GroupProps)[];
|
||||
};
|
||||
|
||||
export type BaseSortableData =
|
||||
| BaseSortableRoot
|
||||
| BaseSortableGroup
|
||||
| BaseSortableItem;
|
||||
|
||||
export type SortableData<GroupProps = unknown, ItemProps = unknown> =
|
||||
| BaseSortableRoot<GroupProps>
|
||||
| (BaseSortableGroup<GroupProps, ItemProps> & GroupProps)
|
||||
| (BaseSortableItem & ItemProps);
|
||||
|
||||
export type ExtractGroup<T> = T extends { type: 'group' } ? T : never;
|
||||
export type ExtractItem<T> = T extends { type: 'item' } ? T : never;
|
||||
export type SortableLeafItem<ItemProps = unknown> = {
|
||||
key: Id;
|
||||
} & ItemProps;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { MonitorPicker, MonitorPickerOld } from '../MonitorPicker';
|
||||
import { MonitorPicker } from '../MonitorPicker';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { LuMinusCircle, LuPlus } from 'react-icons/lu';
|
||||
@ -23,17 +23,11 @@ import { domainRegex, slugRegex } from '@tianji/shared';
|
||||
import { useElementSize } from '@/hooks/useResizeObserver';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { ColorTag } from '@/components/ColorTag';
|
||||
import { Collapsible, CollapsibleContent } from '@/components/ui/collapsible';
|
||||
import { CollapsibleTrigger } from '@radix-ui/react-collapsible';
|
||||
import { CaretSortIcon } from '@radix-ui/react-icons';
|
||||
import { DeprecatedBadge } from '@/components/DeprecatedBadge';
|
||||
import { groupItemSchema, MonitorStatusPageServiceList } from './ServiceList';
|
||||
|
||||
const Text = Typography.Text;
|
||||
|
||||
@ -46,6 +40,15 @@ const editFormSchema = z.object({
|
||||
.regex(domainRegex, 'Invalid domain')
|
||||
.or(z.literal(''))
|
||||
.optional(),
|
||||
body: z
|
||||
.object({
|
||||
groups: z.array(groupItemSchema),
|
||||
})
|
||||
.default({ groups: [] }),
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
monitorList: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
@ -77,10 +80,20 @@ export const MonitorStatusPageEditForm: React.FC<MonitorStatusPageEditFormProps>
|
||||
description: '',
|
||||
domain: '',
|
||||
monitorList: [],
|
||||
body: { groups: [] },
|
||||
},
|
||||
});
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
const showDeprecatedMonitorList = props.initialValues
|
||||
? Array.isArray(props.initialValues.monitorList) &&
|
||||
props.initialValues.monitorList.length > 0
|
||||
: false;
|
||||
|
||||
const {
|
||||
fields: oldMonitorFields,
|
||||
append,
|
||||
remove,
|
||||
} = useFieldArray({
|
||||
control: form.control,
|
||||
name: 'monitorList',
|
||||
keyName: 'key',
|
||||
@ -191,14 +204,37 @@ export const MonitorStatusPageEditForm: React.FC<MonitorStatusPageEditFormProps>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
|
||||
{/* Body */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="body.groups"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('Body')}</FormLabel>
|
||||
<FormControl>
|
||||
<MonitorStatusPageServiceList
|
||||
{...field}
|
||||
value={field.value ?? []}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* MonitorList */}
|
||||
{showDeprecatedMonitorList && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="monitorList"
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('Monitor List')}</FormLabel>
|
||||
{fields.map((field, i) => (
|
||||
<FormItem className="opacity-50">
|
||||
<FormLabel>
|
||||
{t('Monitor List')}
|
||||
<DeprecatedBadge tip={t('Please use Body field')} />
|
||||
</FormLabel>
|
||||
{oldMonitorFields.map((field, i) => (
|
||||
<>
|
||||
{i !== 0 && <Separator />}
|
||||
|
||||
@ -259,6 +295,7 @@ export const MonitorStatusPageEditForm: React.FC<MonitorStatusPageEditFormProps>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="!mt-8 flex justify-end gap-2">
|
||||
<Button type="submit" loading={isLoading}>
|
||||
|
@ -1,59 +1,243 @@
|
||||
import React, { useState } from 'react';
|
||||
import { SortableData } from '@/components/Sortable/types';
|
||||
import React from 'react';
|
||||
import { SortableItem } from '@/components/Sortable/types';
|
||||
import { SortableGroup } from '@/components/Sortable/SortableGroup';
|
||||
import { z } from 'zod';
|
||||
import { useEvent } from '@/hooks/useEvent';
|
||||
import { v1 as uuid } from 'uuid';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
import { LuMinusCircle, LuPlusCircle, LuTrash } from 'react-icons/lu';
|
||||
import { cn } from '@/utils/style';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { MonitorPicker } from '../MonitorPicker';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { set } from 'lodash-es';
|
||||
|
||||
type MonitorStatusPageServiceItem = SortableData<{}, { title: string }>;
|
||||
export const leafItemSchema = z.object({
|
||||
key: z.string(),
|
||||
id: z.string(),
|
||||
type: z.enum(['monitor']),
|
||||
showCurrent: z.boolean().default(false).optional(),
|
||||
});
|
||||
|
||||
export const MonitorStatusPageServiceList: React.FC = React.memo(() => {
|
||||
const [list, setList] = useState<MonitorStatusPageServiceItem[]>([
|
||||
export const groupItemSchema = z.object({
|
||||
key: z.string(),
|
||||
title: z.string(),
|
||||
children: z.array(leafItemSchema),
|
||||
});
|
||||
|
||||
type GroupItemProps = Omit<z.infer<typeof groupItemSchema>, 'key' | 'children'>;
|
||||
type LeafItemProps = Omit<z.infer<typeof leafItemSchema>, 'key'>;
|
||||
|
||||
export type MonitorStatusPageServiceItem = SortableItem<
|
||||
GroupItemProps,
|
||||
LeafItemProps
|
||||
>;
|
||||
|
||||
interface MonitorStatusPageServiceListProps {
|
||||
value: MonitorStatusPageServiceItem[];
|
||||
onChange: (list: MonitorStatusPageServiceItem[]) => void;
|
||||
}
|
||||
export const MonitorStatusPageServiceList: React.FC<MonitorStatusPageServiceListProps> =
|
||||
React.memo((props) => {
|
||||
const { t } = useTranslation();
|
||||
const handleAddGroup = useEvent(() => {
|
||||
props.onChange([
|
||||
...props.value,
|
||||
{
|
||||
type: 'group',
|
||||
title: 'Group 1',
|
||||
id: 'group1',
|
||||
items: [
|
||||
key: uuid(),
|
||||
title: 'Default',
|
||||
children: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
const handleAddItem = useEvent((groupKey: 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].children = [
|
||||
...newList[index].children,
|
||||
{
|
||||
id: 'item1',
|
||||
type: 'item',
|
||||
title: 'Item 1',
|
||||
key: uuid(),
|
||||
id: '',
|
||||
type: 'monitor',
|
||||
showCurrent: false,
|
||||
},
|
||||
{
|
||||
id: 'item2',
|
||||
type: 'item',
|
||||
title: 'Item 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
title: 'Group 2',
|
||||
id: 'group2',
|
||||
items: [
|
||||
{
|
||||
id: 'item3',
|
||||
type: 'item',
|
||||
title: 'Item 3',
|
||||
},
|
||||
{
|
||||
id: 'item4',
|
||||
type: 'item',
|
||||
title: 'Item 4',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as MonitorStatusPageServiceItem[]);
|
||||
] as MonitorStatusPageServiceItem[];
|
||||
|
||||
props.onChange(newList);
|
||||
});
|
||||
|
||||
const handleDeleteGroup = useEvent((groupKey: string) => {
|
||||
const index = props.value.findIndex((item) => item.key === groupKey);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newList = [...props.value];
|
||||
|
||||
newList.splice(index, 1);
|
||||
|
||||
props.onChange(newList);
|
||||
});
|
||||
|
||||
const handleDeleteItem = useEvent((groupKey: string, itemKey: string) => {
|
||||
const newList = [...props.value];
|
||||
const groupIndex = newList.findIndex((item) => item.key === groupKey);
|
||||
if (groupIndex === -1) {
|
||||
return;
|
||||
}
|
||||
const group = newList[groupIndex];
|
||||
if (!('children' in group)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemIndex = group.children.findIndex(
|
||||
(item) => item.key === itemKey
|
||||
);
|
||||
if (itemIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
group.children.splice(itemIndex, 1);
|
||||
|
||||
props.onChange(newList);
|
||||
});
|
||||
|
||||
const handleUpdateItem = useEvent(
|
||||
(groupKey: string, itemKey: string, fieldName: string, value: any) => {
|
||||
const newList = [...props.value];
|
||||
const groupIndex = newList.findIndex((item) => item.key === groupKey);
|
||||
if (groupIndex === -1) {
|
||||
return;
|
||||
}
|
||||
const group = newList[groupIndex];
|
||||
if (!('children' in group)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemIndex = group.children.findIndex(
|
||||
(item) => item.key === itemKey
|
||||
);
|
||||
if (itemIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(group, `children[${itemIndex}].${fieldName}`, value);
|
||||
|
||||
props.onChange(newList);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<SortableGroup
|
||||
list={list}
|
||||
onChange={(list) => setList(list)}
|
||||
renderGroup={(group, children) => (
|
||||
<div className="rounded-lg border p-2">
|
||||
<SortableGroup<GroupItemProps, LeafItemProps>
|
||||
list={props.value}
|
||||
onChange={props.onChange}
|
||||
renderGroup={(group, children, level) => (
|
||||
<div>
|
||||
<div>{group.title}</div>
|
||||
<div className="p-2">{children}</div>
|
||||
<div className={cn('flex items-center gap-2')}>
|
||||
<span>{group.title}</span>
|
||||
|
||||
{level > 0 && (
|
||||
<>
|
||||
<Button
|
||||
className="h-6 w-6"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
type="button"
|
||||
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>
|
||||
<div
|
||||
className={cn(level > 0 && 'border-l-4 border-gray-600 p-2')}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
renderItem={(item) => <div>{item.id}</div>}
|
||||
renderItem={(item, i, group) => {
|
||||
if (item.type === 'monitor') {
|
||||
return (
|
||||
<div key={item.key}>
|
||||
{i !== 0 && <Separator />}
|
||||
|
||||
<div className="mb-2 flex flex-col gap-2">
|
||||
<MonitorPicker
|
||||
value={item.id}
|
||||
onValueChange={(val) =>
|
||||
handleUpdateItem(group.key, item.key, 'id', val)
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="flex flex-1 items-center">
|
||||
<Switch
|
||||
checked={item.showCurrent ?? false}
|
||||
onCheckedChange={(val) =>
|
||||
handleUpdateItem(
|
||||
group.key,
|
||||
item.key,
|
||||
'showCurrent',
|
||||
val
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<span className="ml-1 flex-1 align-middle text-sm">
|
||||
{t('Show Latest Value')}
|
||||
</span>
|
||||
|
||||
<LuMinusCircle
|
||||
className="cursor-pointer text-lg"
|
||||
onClick={() => handleDeleteItem(group.key, item.key)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}}
|
||||
/>
|
||||
|
||||
{Array.isArray(props.value) && props.value.length === 0 && (
|
||||
<p className="text-xs opacity-50">
|
||||
{t('No any group has been created, click button to create one')}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Button
|
||||
className="mt-2"
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleAddGroup}
|
||||
>
|
||||
{t('Add Group')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
MonitorStatusPageServiceList.displayName = 'MonitorStatusPageServiceList';
|
||||
|
@ -1,6 +1,10 @@
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router';
|
||||
import { isDev } from '@/utils/env';
|
||||
import { MonitorStatusPageServiceList } from '@/components/monitor/StatusPage/ServiceList';
|
||||
import {
|
||||
MonitorStatusPageServiceItem,
|
||||
MonitorStatusPageServiceList,
|
||||
} from '@/components/monitor/StatusPage/ServiceList';
|
||||
import { useState } from 'react';
|
||||
|
||||
export const Route = createFileRoute('/playground')({
|
||||
beforeLoad: () => {
|
||||
@ -13,13 +17,35 @@ export const Route = createFileRoute('/playground')({
|
||||
component: PageComponent,
|
||||
});
|
||||
|
||||
function PageComponent(this: {
|
||||
beforeLoad: () => void;
|
||||
component: () => import('react/jsx-runtime').JSX.Element;
|
||||
}) {
|
||||
function PageComponent() {
|
||||
const [list, setList] = useState<MonitorStatusPageServiceItem[]>([
|
||||
{
|
||||
title: 'Group 1',
|
||||
key: 'group1',
|
||||
children: [
|
||||
{
|
||||
key: 'item1',
|
||||
id: 'fooo',
|
||||
type: 'monitor',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Group 2',
|
||||
key: 'group2',
|
||||
children: [
|
||||
{
|
||||
key: 'item2',
|
||||
id: 'barr',
|
||||
type: 'monitor',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MonitorStatusPageServiceList />
|
||||
<MonitorStatusPageServiceList value={list} onChange={setList} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user