diff --git a/src/GraphWorkspace.jsx b/src/GraphWorkspace.jsx index 3b5a98e..bccfc69 100644 --- a/src/GraphWorkspace.jsx +++ b/src/GraphWorkspace.jsx @@ -1,7 +1,7 @@ import React from 'react'; import ZoomComp from './component/ZoomSetter'; - -// import { actionType as T } from './reducer'; +import ConfirmModal from './component/modals/ConfirmModal'; +import { actionType as T } from './reducer'; import './graphWorkspace.css'; // import localStorageManager from './graph-builder/local-storage-manager'; import TabBar from './component/TabBar'; @@ -80,6 +80,16 @@ const GraphComp = (props) => { ))} + { + if (superState.confirmModal.onConfirm) superState.confirmModal.onConfirm(); + dispatcher({ type: T.SET_CONFIRM_MODAL, payload: { open: false, message: '', onConfirm: null } }); + }} + onCancel={() => dispatcher({ type: T.SET_CONFIRM_MODAL, payload: { open: false, message: '', onConfirm: null } })} + /> ); }; diff --git a/src/component/FullScreenButton.jsx b/src/component/FullScreenButton.jsx index aa7bc2b..19f12e6 100644 --- a/src/component/FullScreenButton.jsx +++ b/src/component/FullScreenButton.jsx @@ -15,7 +15,11 @@ export default function FullScreenButton() { }; return ( - ); diff --git a/src/component/HeaderComps.jsx b/src/component/HeaderComps.jsx index 3062785..07cbf36 100644 --- a/src/component/HeaderComps.jsx +++ b/src/component/HeaderComps.jsx @@ -37,7 +37,7 @@ const Switcher = ({ tabIndex={tabIndex} className={`tool ${active ? 'active' : ''}`} onClick={action} - onKeyDown={(ev) => ev.key === ' ' && action()} + onKeyDown={(ev) => (ev.key === ' ' || ev.key === 'Enter') && action()} > {Icon &&
} (active && action())} - onKeyDown={(ev) => active && ev.key === ' ' && action()} + onKeyDown={(ev) => active && (ev.key === ' ' || ev.key === 'Enter') && action()} data-tip={hotkey ? hotkey.split(',')[0] : ''} style={{ display: `${visibility ? '' : 'none'}` }} > diff --git a/src/component/TabBar.jsx b/src/component/TabBar.jsx index 7bdeaa5..110fed0 100644 --- a/src/component/TabBar.jsx +++ b/src/component/TabBar.jsx @@ -79,6 +79,7 @@ const TabBar = ({ superState, dispatcher }) => { onClick={newProject.bind(this, superState, dispatcher)} type="button" id="new_graph" + aria-label="New workflow tab" data-tip="New Workflow Tab (Ctrl + Shift + M)" > @@ -88,7 +89,7 @@ const TabBar = ({ superState, dispatcher }) => { key={el.graphID} className={`tab tab-graph ${superState.curGraphIndex === i ? 'selected' : 'none'}`} onClick={() => dispatcher({ type: T.CHANGE_TAB, payload: i })} - onKeyDown={(ev) => ev.key === ' ' && dispatcher({ type: T.CHANGE_TAB, payload: i })} + onKeyDown={(ev) => (ev.key === ' ' || ev.key === 'Enter') && dispatcher({ type: T.CHANGE_TAB, payload: i })} role="button" tabIndex={0} id={`tab_${i}`} @@ -102,6 +103,7 @@ const TabBar = ({ superState, dispatcher }) => { className="tab-act edit" onClick={editCur} type="button" + aria-label="Edit workflow details" data-tip="Edit Workflow Details (Ctrl + Shift + E)" data-for="header-tab" > @@ -112,6 +114,7 @@ const TabBar = ({ superState, dispatcher }) => { className="tab-act close" onClick={handleRequestCloseTab.bind(this, i)} type="button" + aria-label="Close workflow tab" data-tip="Close current Workflow (Ctrl + Shift + L)" data-for="header-tab" > diff --git a/src/component/ZoomSetter.jsx b/src/component/ZoomSetter.jsx index f8d26e9..9cc902a 100644 --- a/src/component/ZoomSetter.jsx +++ b/src/component/ZoomSetter.jsx @@ -19,9 +19,10 @@ const ZoomComp = ({ superState, dispatcher }) => {
myGraph.resetZoom()} - onKeyDown={(ev) => ev.key === ' ' && (myGraph.resetZoom())} + onKeyDown={(ev) => (ev.key === ' ' || ev.key === 'Enter') && myGraph.resetZoom()} > @@ -29,9 +30,10 @@ const ZoomComp = ({ superState, dispatcher }) => {
myGraph.fitZoom()} - onKeyDown={(ev) => ev.key === ' ' && (myGraph.resetZoom())} + onKeyDown={(ev) => (ev.key === ' ' || ev.key === 'Enter') && myGraph.fitZoom()} > diff --git a/src/component/modals/History.jsx b/src/component/modals/History.jsx index fba9b4c..8b9646a 100644 --- a/src/component/modals/History.jsx +++ b/src/component/modals/History.jsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react'; import Modal from './ParentModal'; +import ConfirmModal from './ConfirmModal'; import './settings.css'; import { actionType as T } from '../../reducer'; import GA from '../../graph-builder/graph-actions'; @@ -21,6 +22,8 @@ const HistoryModal = ({ superState, dispatcher }) => { return res; }; const [filterAction, setFilterAction] = useState(mapActionToTrue()); + const [restoreConfirmOpen, setRestoreConfirmOpen] = useState(false); + const [pendingRestoreIndex, setPendingRestoreIndex] = useState(null); const getLabelFromID = (x) => { if (superState.curGraphInstance) { @@ -71,20 +74,33 @@ const HistoryModal = ({ superState, dispatcher }) => { [GA.SET_BENDW]: 'EdgeBend', }; - const restoreState = (index) => { - // eslint-disable-next-line no-alert - if (window.confirm('Are you sure to restore the selected state?')) { - let tempCurState = curState; - while (index > tempCurState) { - superState.curGraphInstance.undoSingleAction(); - tempCurState += 1; - } - while (index < tempCurState) { - superState.curGraphInstance.redoSingleAction(); - tempCurState -= 1; - } - setcurState(tempCurState); + const doRestore = (index) => { + let tempCurState = curState; + while (index > tempCurState) { + superState.curGraphInstance.undoSingleAction(); + tempCurState += 1; + } + while (index < tempCurState) { + superState.curGraphInstance.redoSingleAction(); + tempCurState -= 1; } + setcurState(tempCurState); + }; + + const restoreState = (index) => { + setPendingRestoreIndex(index); + setRestoreConfirmOpen(true); + }; + + const handleRestoreConfirm = () => { + doRestore(pendingRestoreIndex); + setRestoreConfirmOpen(false); + setPendingRestoreIndex(null); + }; + + const handleRestoreCancel = () => { + setRestoreConfirmOpen(false); + setPendingRestoreIndex(null); }; const prefixTid = (tid, str, authorName, index, hash) => { const DT = new Date(parseInt(tid, 10)); @@ -127,11 +143,19 @@ const HistoryModal = ({ superState, dispatcher }) => { const close = () => dispatcher({ type: T.SET_HISTORY_MODAL, payload: false }); return ( - + <> + +
Filters @@ -168,7 +192,8 @@ const HistoryModal = ({ superState, dispatcher }) => {
- + + ); }; diff --git a/src/graph-builder/graph-core/2-canvas.js b/src/graph-builder/graph-core/2-canvas.js index 348ff53..863297a 100644 --- a/src/graph-builder/graph-core/2-canvas.js +++ b/src/graph-builder/graph-core/2-canvas.js @@ -31,14 +31,20 @@ class GraphCanvas extends Core { } clearAll() { - if (this.cy.elements().length === 0) return true; - // eslint-disable-next-line no-alert - if (!window.confirm('Do want to clear all elements?')) return false; - this.cy.elements().forEach((el) => this.deleteElem(el.id(), 0)); - // this.actionArr = []; - this.dispatcher({ type: T.CHANGE_RESET, payload: true }); - this.cy.emit('graph-modified'); - return true; + if (this.cy.elements().length === 0) return; + this.dispatcher({ + type: T.SET_CONFIRM_MODAL, + payload: { + open: true, + message: 'Do you want to clear all elements?', + onConfirm: () => { + this.cy.elements().forEach((el) => this.deleteElem(el.id(), 0)); + // this.actionArr = []; + this.dispatcher({ type: T.CHANGE_RESET, payload: true }); + this.cy.emit('graph-modified'); + }, + }, + }); } resetAllComp() { diff --git a/src/graph-builder/graph-core/6-server.js b/src/graph-builder/graph-core/6-server.js index e3fc1d3..d396bbb 100644 --- a/src/graph-builder/graph-core/6-server.js +++ b/src/graph-builder/graph-core/6-server.js @@ -86,30 +86,31 @@ class GraphServer extends GraphLoadSave { } forcePushToServer() { - // eslint-disable-next-line - if (!window.confirm( - 'Forced push may result in workflow overwite and loss of changes pushed by others. Confirm?', - )) return; - if (this.serverID) { - forceUpdateGraph(this.serverID, this.getGraphML()).then(() => { - - }).catch((err) => { - toast.error(err.response?.data?.message || err.message); - }); - } else { - postGraph(this.getGraphML()).then((serverID) => { - this.set({ serverID }); - }).catch((err) => { - toast.error(err.response?.data?.message || err.message); - }); - } + this.dispatcher({ + type: T.SET_CONFIRM_MODAL, + payload: { + open: true, + message: 'Forced push may result in workflow overwite and loss of changes pushed by others. Confirm?', + onConfirm: () => { + if (this.serverID) { + forceUpdateGraph(this.serverID, this.getGraphML()).then(() => { + + }).catch((err) => { + toast.error(err.response?.data?.message || err.message); + }); + } else { + postGraph(this.getGraphML()).then((serverID) => { + this.set({ serverID }); + }).catch((err) => { + toast.error(err.response?.data?.message || err.message); + }); + } + }, + }, + }); } forcePullFromServer() { - // eslint-disable-next-line - if (!window.confirm( - 'Forced pull may result in workflow overwite and loss of unsaved changes. Confirm?', - )) return; if (this.serverID) { getGraph(this.serverID).then((graphXML) => { this.setGraphML(graphXML); diff --git a/src/reducer/actionType.js b/src/reducer/actionType.js index ea540e1..d2b9cef 100644 --- a/src/reducer/actionType.js +++ b/src/reducer/actionType.js @@ -46,6 +46,7 @@ const actionType = { SET_LOGS_MESSAGE: 'SET_LOGS_MESSAGE', SET_GRAPH_INSTANCE: 'SET_GRAPH_INSTANCE', TOGGLE_DARK_MODE: 'TOGGLE_DARK_MODE', + SET_CONFIRM_MODAL: 'SET_CONFIRM_MODAL', }; export default zealit(actionType); diff --git a/src/reducer/initialState.js b/src/reducer/initialState.js index 4fb1993..736c18c 100644 --- a/src/reducer/initialState.js +++ b/src/reducer/initialState.js @@ -39,6 +39,7 @@ const initialState = { logs: false, logsmessage: '', darkMode: false, + confirmModal: { open: false, message: '', onConfirm: null }, }; const initialGraphState = { diff --git a/src/reducer/reducer.js b/src/reducer/reducer.js index e7419ff..c83c5bc 100644 --- a/src/reducer/reducer.js +++ b/src/reducer/reducer.js @@ -73,6 +73,7 @@ const reducer = (state, action) => { case T.ELE_SELECTED: return { ...state, eleSelected: true, eleSelectedPayload: action.payload }; case T.ELE_UNSELECTED: return { ...state, eleSelected: false }; case T.TURN_DRAW: return { ...state, drawModeOn: action.payload }; + case T.SET_CONFIRM_MODAL: return { ...state, confirmModal: action.payload }; case T.SET_UNDO: return { ...state, undoEnabled: action.payload }; case T.SET_REDO: return { ...state, redoEnabled: action.payload };