Confirm delete, edit modals, some cleanup.

remotes/origin/refactor/jwt-ls
Artem Golub 3 years ago
parent 7bc6892865
commit b3f3fd6a39

@ -1,5 +1,7 @@
# Container ToolKit
![Alt text](/screenshots/ui.png?raw=true "UI")
## Local setup and development
On a Mac/Linux/Windows you need Docker, Docker Compose installed. Optionally GCC to run make commands for convenience, or just run the commands from the Makefile by hand.

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

BIN
services/.DS_Store vendored

Binary file not shown.

@ -0,0 +1,22 @@
import { TrashIcon, PencilIcon } from "@heroicons/react/solid";
export const Popover = ({
onEditClick,
onDeleteClick
}: {
onEditClick: Function
onDeleteClick: Function
}) => {
return (
<div className="relative flex flex-col items-center">
<div className="flex absolute -bottom-2 flex flex-col items-center p-2">
<span className="relative z-10 p-2.5 text-xs leading-none text-white whitespace-no-wrap bg-gray-700 shadow-lg rounded-md">
<div className="flex space-x-2.5">
<TrashIcon onClick={() => onDeleteClick()} className="w-3 h-3 text-red-400"></TrashIcon>
<PencilIcon onClick={() => onEditClick()} className="w-3 h-3"></PencilIcon>
</div>
</span>
<div className="w-3 h-3 -mt-2.5 rotate-45 bg-gray-600"></div>
</div>
</div>
);
};

@ -14,10 +14,11 @@ import {
position
} from "../../reducers";
import Remove from "../Remove";
import eventBus from "../../events/eventBus";
import { Popover } from "./Popover";
import ModalConfirmDelete from "../Modal/Service/ConfirmDelete";
import ModalServiceCreate from "../Modal/Service/Create";
import ModalServiceEdit from "../Modal/Service/Edit";
import ModalCreate from "../Modal/Node/Create";
import ModalEdit from "../Modal/Node/Edit";
import { useClickOutside } from "../../utils/clickOutside";
import { IClientNodeItem, IGraphData } from "../../types";
import { nodeLibraries } from "../../utils/data/libraries";
@ -47,17 +48,21 @@ export const Canvas: FC<ICanvasProps> = (props) => {
const [instanceConnections, setInstanceConnections] = useState(state.connections);
const [copyText, setCopyText] = useState("Copy");
const [selectedNode, setSelectedNode] = useState<IClientNodeItem | null>(null);
const [showModalCreateStep, setShowModalCreateStep] = useState(false);
const [showModalEditStep, setShowModalEditStep] = useState(false);
const [showModalCreate, setShowModalCreate] = useState(false);
const [showModalEdit, setShowModalEdit] = useState(false);
const [showModalCreateService, setShowModalCreateService] = useState(false);
const [showModalEditService, setShowModalEditService] = useState(false);
const [showModalConfirmDeleteService, setShowModalConfirmDeleteService] = useState(false);
const [showVolumesModal, setShowVolumesModal] = useState(false);
const [showNetworksModal, setShowNetworksModal] = useState(false);
const [nodeDragging, setNodeDragging] = useState<string | null>();
const [nodeHovering, setNodeHovering] = useState<string | null>();
const [dragging, setDragging] = useState(false);
const [_scale, _setScale] = useState(1);
const [_left, _setLeft] = useState(0);
const [_top, _setTop] = useState(0);
const [_initX, _setInitX] = useState(0);
const [_initY, _setInitY] = useState(0);
const [containerCallbackRef, setZoom, setStyle, removeEndpoint] = useJsPlumb(
instanceNodes,
instanceConnections,
@ -76,8 +81,7 @@ export const Canvas: FC<ICanvasProps> = (props) => {
stateRef.current = state.nodes;
useClickOutside(drop, () => {
setShowModalCreateStep(false);
setShowModalCreate(false);
setShowModalCreateService(false);
});
useEffect(() => {
@ -256,38 +260,49 @@ export const Canvas: FC<ICanvasProps> = (props) => {
_setScale(state.canvasPosition.scale);
}, [state.canvasPosition]);
useEffect(() => {
eventBus.on("EVENT_DRAG_START", (data: any) => {
setNodeDragging(data.detail.message.id);
});
eventBus.on("EVENT_DRAG_STOP", (data: any) => {
setNodeDragging(null);
});
return () => {
eventBus.remove("EVENT_DRAG_START", () => {});
eventBus.remove("EVENT_DRAG_STOP", () => { });
};
}, []);
return (
<>
{showModalCreateStep
{showModalCreateService
? <ModalServiceCreate
onHide={() => setShowModalCreateStep(false)}
onHide={() => setShowModalCreateService(false)}
onAddEndpoint={(values: any) => onAddEndpoint(values)}
/>
: null
}
{showModalEditStep
{showModalEditService
? <ModalServiceEdit
node={selectedNode}
onHide={() => setShowModalEditStep(false)}
onHide={() => setShowModalEditService(false)}
onUpdateEndpoint={(values: any) => onUpdateEndpoint(values)}
/>
: null
}
{showModalCreate
? <ModalCreate
onHide={() => setShowModalCreate(false)}
onAddEndpoint={(values: any) => onAddEndpoint(values)}
/>
: null
{showModalConfirmDeleteService
? <ModalConfirmDelete
onHide={() => setShowModalConfirmDeleteService(false)}
onConfirm={() => {
setShowModalEditService(false);
if (selectedNode) {
onRemoveEndpoint(selectedNode.key);
}
{showModalEdit
? <ModalEdit
node={selectedNode}
onHide={() => setShowModalEdit(false)}
onUpdateEndpoint={(nodeItem: IClientNodeItem) => onUpdateEndpoint(nodeItem)}
}}
/>
: null
}
@ -300,14 +315,14 @@ export const Canvas: FC<ICanvasProps> = (props) => {
<div className="flex space-x-2 p-2">
<button className="hidden btn-util" type="button" onClick={zoomOut} disabled={scale <= 0.5}>-</button>
<button className="hidden btn-util" type="button" onClick={zoomIn} disabled={scale >= 1}>+</button>
<button className="flex space-x-1 btn-util" type="button" onClick={() => setShowModalCreateStep(true)}>
<button className="flex space-x-1 btn-util" type="button" onClick={() => setShowModalCreateService(true)}>
<PlusIcon className="w-3"/>
<span>Service</span>
</button>
<button className="btn-util" type="button" onClick={() => setShowModalCreate(true)}>
<button className="btn-util" type="button" onClick={() => setShowVolumesModal(true)}>
Volumes
</button>
<button className="btn-util" type="button" onClick={() => setShowModalCreate(true)}>
<button className="btn-util" type="button" onClick={() => setShowNetworksModal(true)}>
Networks
</button>
</div>
@ -335,10 +350,28 @@ export const Canvas: FC<ICanvasProps> = (props) => {
{values(instanceNodes).map((x) => (
<div
key={x.key}
className={"node-item cursor-pointer shadow flex flex-col"}
className={"node-item cursor-pointer shadow flex flex-col group"}
id={x.key}
style={{ top: x.position.top, left: x.position.left }}
onMouseEnter={() => setNodeHovering(x.key)}
onMouseLeave={() => {
if (nodeHovering === x.key) {
setNodeHovering(null);
}
}}
>
{((nodeHovering === x.key) && (nodeDragging !== x.key)) &&
<Popover
onEditClick={() => {
setSelectedNode(x);
setShowModalEditService(true);
}}
onDeleteClick={() => {
setSelectedNode(x);
setShowModalConfirmDeleteService(true);
}}
></Popover>
}
<div className="node-label w-full py-2 px-4">
<div className="text-sm font-semibold">
{x.configuration.prettyName}

@ -0,0 +1,71 @@
import { XIcon, ExclamationIcon } from "@heroicons/react/outline";
interface IModalConfirmDeleteProps {
onConfirm: any;
onHide: any;
}
const ModalConfirmDelete = (props: IModalConfirmDeleteProps) => {
const { onConfirm, onHide } = props;
return (
<div className="fixed z-50 inset-0 overflow-y-auto">
<div className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 outline-none focus:outline-none">
<div onClick={onHide} className="opacity-25 fixed inset-0 z-40 bg-black"></div>
<div className="relative w-auto my-6 mx-auto max-w-5xl z-50">
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
<div className="flex items-center justify-between px-4 py-3 border-b border-solid border-blueGray-200 rounded-t">
<h3 className="text-sm font-semibold">Confirm delete</h3>
<button
className="p-1 ml-auto text-black float-right outline-none focus:outline-none"
onClick={onHide}
>
<span className="block outline-none focus:outline-none">
<XIcon className="w-4" />
</span>
</button>
</div>
<div className="relative px-4 py-3 flex-auto">
<div className="sm:flex sm:items-start">
<div className="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<ExclamationIcon className="h-6 w-6 text-red-600" aria-hidden="true" />
</div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<div className="mt-2">
<p className="text-sm dark:text-gray-100 text-gray-500">
Careful! This action cannot be undone.
</p>
</div>
</div>
</div>
</div>
<div className="flex items-center justify-end px-4 py-3 border-t border-solid border-blueGray-200 rounded-b">
<button
type="button"
className="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-3 py-1 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 sm:mt-0 sm:w-auto sm:text-sm"
onClick={onHide}
>
Cancel
</button>
<button
type="button"
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-3 py-1 bg-red-600 text-sm font-medium text-white hover:bg-red-700 sm:ml-3 sm:w-auto sm:text-sm"
onClick={() => {
onHide()
onConfirm()
}}
>
Delete
</button>
</div>
</div>
</div>
</div>
</div>
)
}
export default ModalConfirmDelete;

@ -27,7 +27,6 @@ const ModalServiceCreate = (props: IModalServiceProps) => {
});
return (
<>
<div className="fixed z-50 inset-0 overflow-y-auto">
<div className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 outline-none focus:outline-none">
<div onClick={onHide} className="opacity-25 fixed inset-0 z-40 bg-black"></div>
@ -98,7 +97,6 @@ const ModalServiceCreate = (props: IModalServiceProps) => {
</div>
</div>
</div>
</>
);
}

@ -13,8 +13,10 @@ import {
import {
BrowserJsPlumbInstance,
newInstance,
EVENT_DRAG_START,
EVENT_DRAG_STOP,
EVENT_CONNECTION_DBL_CLICK
EVENT_CONNECTION_DBL_CLICK,
DragStartPayload
} from "@jsplumb/browser-ui";
import {
defaultOptions,
@ -23,6 +25,7 @@ import {
sourceEndpoint,
targetEndpoint
} from "../utils/options";
import eventBus from "../events/eventBus";
import { getConnections } from "../utils";
import { IClientNodeItem } from "../types";
import { Dictionary, isEqual } from "lodash";
@ -90,11 +93,13 @@ export const useJsPlumb = (
if (nodeConnections) {
Object.values(nodeConnections).forEach((conn) => {
instance.destroyConnector(conn);
instance.deleteConnection(conn);
});
};
instance.removeAllEndpoints(document.getElementById(node.key) as Element);
instance.repaintEverything();
}, [instance]);
const getAnchors = (port: string[], anchorIds: AnchorId[]): IAnchor[] => {
@ -234,7 +239,7 @@ export const useJsPlumb = (
'connections': getConnections(instance.getConnections({}, true) as Connection[])
});
}
}, [instance, addEndpoints, onGraphUpdate]);
}, [instance, addEndpoints, stateRef.current]);
useEffect(() => {
if (!instance) return;
@ -263,6 +268,14 @@ export const useJsPlumb = (
container: containerRef.current
});
jsPlumbInstance.bind(EVENT_DRAG_START, function (params: DragStartPayload) {
eventBus.dispatch("EVENT_DRAG_START", { message: { "id": params.el.id } });
});
jsPlumbInstance.bind(EVENT_DRAG_STOP, function (params: DragStartPayload) {
eventBus.dispatch("EVENT_DRAG_STOP", { message: {"id": params.el.id} });
});
jsPlumbInstance.bind(INTERCEPT_BEFORE_DROP, function (params: BeforeDropParams) {
return onbeforeDropIntercept(jsPlumbInstance, params);
});
@ -318,7 +331,6 @@ export const useJsPlumb = (
return () => {
reset();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return [containerCallbackRef, setZoom, setStyle, removeEndpoint];

@ -0,0 +1,13 @@
const eventBus = {
on(event: string, callback: { (data: any): void; (data: any): void; (arg: any): any; }) {
document.addEventListener(event, (e) => callback(e));
},
dispatch(event: string, data: { message: { id: string; } | { id: string; }; }) {
document.dispatchEvent(new CustomEvent(event, { detail: data }));
},
remove(event: string, callback: { (): void; (this: Document, ev: any): any; }) {
document.removeEventListener(event, callback);
},
};
export default eventBus;
Loading…
Cancel
Save