From a4ae8f98f157d51d298dda46a38278e79a39a162 Mon Sep 17 00:00:00 2001 From: kunish Date: Tue, 20 Jun 2023 14:31:46 +0800 Subject: [PATCH] feat: drag nodes and subscriptions between groups --- src/components/DraggableResourceBadge.tsx | 62 +++++ src/components/DraggableResourceCard.tsx | 6 +- .../DraggableSubscriptionNodeBadge.tsx | 41 --- src/components/RenameFormModal.tsx | 12 +- src/components/SortableResourceBadge.tsx | 46 ---- src/constants/index.ts | 2 + src/pages/Experiment.tsx | 6 +- src/pages/Orchestrate.tsx | 239 ++++++++++-------- 8 files changed, 215 insertions(+), 199 deletions(-) create mode 100644 src/components/DraggableResourceBadge.tsx delete mode 100644 src/components/DraggableSubscriptionNodeBadge.tsx delete mode 100644 src/components/SortableResourceBadge.tsx diff --git a/src/components/DraggableResourceBadge.tsx b/src/components/DraggableResourceBadge.tsx new file mode 100644 index 00000000..2abcd43d --- /dev/null +++ b/src/components/DraggableResourceBadge.tsx @@ -0,0 +1,62 @@ +import { useDraggable } from '@dnd-kit/core' +import { ActionIcon, Badge, Text, Tooltip } from '@mantine/core' +import { IconX } from '@tabler/icons-react' + +import { DraggableResourceType } from '~/constants' + +export const DraggableResourceBadge = ({ + id, + name, + type, + nodeID, + groupID, + subscriptionID, + onRemove, + dragDisabled, + children, +}: { + id: string + name: string + type: DraggableResourceType + nodeID?: string + groupID?: string + subscriptionID?: string + onRemove?: () => void + dragDisabled?: boolean + children?: React.ReactNode +}) => { + const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ + id, + data: { + type, + nodeID, + groupID, + subscriptionID, + }, + disabled: dragDisabled, + }) + + return ( + {children}} withArrow> + + + + ) + } + style={{ + zIndex: isDragging ? 10 : 0, + }} + opacity={isDragging ? 0.5 : undefined} + > + + {name} + + + + ) +} diff --git a/src/components/DraggableResourceCard.tsx b/src/components/DraggableResourceCard.tsx index eda319b9..2a519364 100644 --- a/src/components/DraggableResourceCard.tsx +++ b/src/components/DraggableResourceCard.tsx @@ -8,6 +8,8 @@ import { DraggableResourceType } from '~/constants' export const DraggableResourceCard = ({ id, + nodeID, + subscriptionID, type, name, onRemove, @@ -15,6 +17,8 @@ export const DraggableResourceCard = ({ children, }: { id: string + nodeID?: string + subscriptionID?: string type: DraggableResourceType name: string onRemove: () => void @@ -22,7 +26,7 @@ export const DraggableResourceCard = ({ children: React.ReactNode }) => { const { t } = useTranslation() - const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ id, data: { type } }) + const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ id, data: { type, nodeID, subscriptionID } }) return ( { - const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ - id, - data: { - subscriptionID, - type: DraggableResourceType.subscription_node, - }, - disabled: dragDisabled, - }) - - return ( - - - {name} - - - ) -} diff --git a/src/components/RenameFormModal.tsx b/src/components/RenameFormModal.tsx index a48dfb34..311b11a0 100644 --- a/src/components/RenameFormModal.tsx +++ b/src/components/RenameFormModal.tsx @@ -23,6 +23,11 @@ export type RenameFormModalRef = { setProps: (props: Props) => void } +export type HandleRenameSubmit = ( + type: RuleType | undefined, + id: string | undefined +) => (values: z.infer) => Promise + export const RenameFormModal = forwardRef( ( { @@ -32,10 +37,7 @@ export const RenameFormModal = forwardRef( }: { opened: boolean onClose: () => void - handleSubmit: ( - type: RuleType | undefined, - id: string | undefined - ) => (values: z.infer) => Promise + handleSubmit: HandleRenameSubmit }, ref ) => { @@ -55,7 +57,7 @@ export const RenameFormModal = forwardRef( if (props.type === RuleType.routing) { return t('routing') } - }, [props.type]) + }, [props.type, t]) useImperativeHandle(ref, () => ({ props, diff --git a/src/components/SortableResourceBadge.tsx b/src/components/SortableResourceBadge.tsx deleted file mode 100644 index ca69656c..00000000 --- a/src/components/SortableResourceBadge.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useSortable } from '@dnd-kit/sortable' -import { CSS } from '@dnd-kit/utilities' -import { ActionIcon, Badge, Text, Tooltip } from '@mantine/core' -import { IconX } from '@tabler/icons-react' - -export const SortableResourceBadge = ({ - id, - name, - onRemove, - dragDisabled, - children, -}: { - id: string - name: string - onRemove: () => void - dragDisabled?: boolean - children?: React.ReactNode -}) => { - const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ - id, - disabled: dragDisabled, - }) - - return ( - {children}} withArrow> - - - - } - style={{ - transform: CSS.Transform.toString(transform), - transition, - zIndex: isDragging ? 10 : 0, - }} - > - - {name} - - - - ) -} diff --git a/src/constants/index.ts b/src/constants/index.ts index 3514e030..af563ad2 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -168,6 +168,8 @@ export enum DraggableResourceType { node = 'node', subscription = 'subscription', subscription_node = 'subscription_node', + groupNode = 'group_node', + groupSubscription = 'group_subscription', } export enum RuleType { diff --git a/src/pages/Experiment.tsx b/src/pages/Experiment.tsx index e56ba44a..d6c4e0c2 100644 --- a/src/pages/Experiment.tsx +++ b/src/pages/Experiment.tsx @@ -37,6 +37,7 @@ import { } from '~/apis' import { ConfigFormDrawer } from '~/components/ConfigFormModal' import { CreateNodeFormModal } from '~/components/CreateNodeFormModal' +import { DraggableResourceBadge } from '~/components/DraggableResourceBadge' import { DraggableResourceCard } from '~/components/DraggableResourceCard' import { DroppableGroupCard } from '~/components/DroppableGroupCard' import { GroupFormModal } from '~/components/GroupFormModal' @@ -45,7 +46,6 @@ import { PlainTextFormModal } from '~/components/PlainTextFormModal' import { RenameFormModal, RenameFormModalRef } from '~/components/RenameFormModal' import { Section } from '~/components/Section' import { SimpleCard } from '~/components/SimpleCard' -import { SortableResourceBadge } from '~/components/SortableResourceBadge' import { DialMode, DraggableResourceType, LogLevel, RuleType } from '~/constants' import { Policy } from '~/schemas/gql/graphql' @@ -438,7 +438,7 @@ export const ExperimentPage = () => { > {nodes.map(({ id: nodeId, name }) => ( - { {subscriptions.map(({ id: subscriptionId, name }) => ( - { const draggingResourceDisplayName = useMemo(() => { if (draggingResource) { - const { type, id, subscriptionID } = draggingResource + const { type, nodeID, groupID, subscriptionID } = draggingResource if (type === DraggableResourceType.node) { - const node = nodesQuery?.nodes.edges.find((node) => node.id === id) + const node = nodesQuery?.nodes.edges.find((node) => node.id === nodeID) return node?.tag } if (type === DraggableResourceType.subscription) { - const subscription = subscriptionsQuery?.subscriptions.find((subscription) => subscription.id === id) + const subscription = subscriptionsQuery?.subscriptions.find( + (subscription) => subscription.id === subscriptionID + ) return subscription?.tag || subscription?.link } @@ -139,12 +140,67 @@ export const OrchestratePage = () => { const subscription = subscriptionsQuery?.subscriptions.find( (subscription) => subscription.id === subscriptionID ) - const node = subscription?.nodes.edges.find((node) => node.id === id) + const node = subscription?.nodes.edges.find((node) => node.id === nodeID) return node?.name } + + if (type === DraggableResourceType.groupNode) { + const group = groupsQuery?.groups.find((group) => group.id === groupID) + + const node = group?.nodes.find((node) => node.id === nodeID) + + return node?.name + } + + if (type === DraggableResourceType.groupSubscription) { + const group = groupsQuery?.groups.find((group) => group.id === groupID) + + const subscription = group?.subscriptions.find((subscription) => subscription.id === subscriptionID) + + return subscription?.tag + } } - }, [draggingResource, nodesQuery, subscriptionsQuery]) + }, [draggingResource, groupsQuery?.groups, nodesQuery?.nodes.edges, subscriptionsQuery?.subscriptions]) + + const onDragStart = (e: DragStartEvent) => { + setDraggingResource({ + ...(e.active.data.current as DraggingResource), + }) + } + + const onDragEnd = (e: DragEndEvent) => { + const { over } = e + + if (over?.id && draggingResource) { + const group = groupsQuery?.groups.find((group) => group.id === over.id) + + if ( + [DraggableResourceType.node, DraggableResourceType.groupNode].includes(draggingResource.type) && + draggingResource?.nodeID && + !group?.nodes.find((node) => node.id === draggingResource.nodeID) + ) { + groupAddNodesMutation.mutate({ id: over.id as string, nodeIDs: [draggingResource.nodeID] }) + } + + if ( + [DraggableResourceType.subscription, DraggableResourceType.groupSubscription].includes(draggingResource.type) && + draggingResource.subscriptionID && + !group?.subscriptions.find((subscription) => subscription.id === draggingResource.subscriptionID) + ) { + groupAddSubscriptionsMutation.mutate({ + id: over.id as string, + subscriptionIDs: [draggingResource.subscriptionID], + }) + } + + if (draggingResource.type === DraggableResourceType.subscription_node && draggingResource.nodeID) { + groupAddNodesMutation.mutate({ id: over.id as string, nodeIDs: [draggingResource.nodeID] }) + } + } + + setDraggingResource(null) + } const [openedRenameFormModal, { open: openRenameFormModal, close: closeRenameFormModal }] = useDisclosure(false) const [openedCreateConfigFormDrawer, { open: openCreateConfigFormDrawer, close: closeCreateConfigFormDrawer }] = @@ -181,6 +237,30 @@ export const OrchestratePage = () => { const renameRoutingMutation = useRenameRoutingMutation() const renameGroupMutation = useRenameGroupMutation() + const handleRenameSubmit: HandleRenameSubmit = (type, id) => async (values) => { + const { name } = values + + if (!type || !id) { + return + } + + if (type === RuleType.config) { + renameConfigMutation.mutate({ id, name }) + } + + if (type === RuleType.dns) { + renameDNSMutation.mutate({ id, name }) + } + + if (type === RuleType.routing) { + renameRoutingMutation.mutate({ id, name }) + } + + if (type === RuleType.group) { + renameGroupMutation.mutate({ id, name }) + } + } + const { defaultConfigID, defaultDNSID, defaultGroupID, defaultRoutingID } = useStore(defaultResourcesAtom) const updateConfigFormDrawerRef = useRef(null) @@ -378,34 +458,7 @@ export const OrchestratePage = () => { /> - { - setDraggingResource({ - id: e.active.id as string, - ...(e.active.data.current as Omit), - }) - }} - onDragEnd={(e) => { - const { over } = e - - if (over?.id && draggingResource?.id) { - if (draggingResource.type === DraggableResourceType.node) { - groupAddNodesMutation.mutate({ id: over.id as string, nodeIDs: [draggingResource.id] }) - } - - if (draggingResource.type === DraggableResourceType.subscription) { - groupAddSubscriptionsMutation.mutate({ id: over.id as string, subscriptionIDs: [draggingResource.id] }) - } - - if (draggingResource.type === DraggableResourceType.subscription_node) { - groupAddNodesMutation.mutate({ id: over.id as string, nodeIDs: [draggingResource.id] }) - } - } - - setDraggingResource(null) - }} - > +
} @@ -477,26 +530,25 @@ export const OrchestratePage = () => { - - - {groupNodes.map(({ id: nodeId, tag, name, subscriptionID }) => ( - - groupDelNodesMutation.mutate({ - id: groupId, - nodeIDs: [nodeId], - }) - } - > - {subscriptionID && - subscriptionsQuery?.subscriptions.find((s) => s.id === subscriptionID)?.tag} - - ))} - - + {groupNodes.map(({ id: nodeId, tag, name, subscriptionID }) => ( + + groupDelNodesMutation.mutate({ + id: groupId, + nodeIDs: [nodeId], + }) + } + > + {subscriptionID && + subscriptionsQuery?.subscriptions.find((s) => s.id === subscriptionID)?.tag} + + ))} @@ -510,23 +562,22 @@ export const OrchestratePage = () => { - - - {groupSubscriptions.map(({ id: subscriptionId, tag, link }) => ( - - groupDelSubscriptionsMutation.mutate({ - id: groupId, - subscriptionIDs: [subscriptionId], - }) - } - /> - ))} - - + {groupSubscriptions.map(({ id: subscriptionId, tag, link }) => ( + + groupDelSubscriptionsMutation.mutate({ + id: groupId, + subscriptionIDs: [subscriptionId], + }) + } + /> + ))} @@ -544,6 +595,7 @@ export const OrchestratePage = () => { removeNodesMutation.mutate([id])} @@ -583,6 +635,7 @@ export const OrchestratePage = () => { } @@ -613,14 +666,16 @@ export const OrchestratePage = () => { {nodes.edges.map(({ id, name }) => ( - {name} - + ))} @@ -735,29 +790,7 @@ export const OrchestratePage = () => { ref={renameFormModalRef} opened={openedRenameFormModal} onClose={closeRenameFormModal} - handleSubmit={(type, id) => async (values) => { - const { name } = values - - if (!type || !id) { - return - } - - if (type === RuleType.config) { - renameConfigMutation.mutate({ id, name }) - } - - if (type === RuleType.dns) { - renameDNSMutation.mutate({ id, name }) - } - - if (type === RuleType.routing) { - renameRoutingMutation.mutate({ id, name }) - } - - if (type === RuleType.group) { - renameGroupMutation.mutate({ id, name }) - } - }} + handleSubmit={handleRenameSubmit} /> )