From 04f9be778ccdbeb2e59a21051a92a947556c4dbc Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 16 Mar 2023 03:08:52 +0800 Subject: [PATCH] ui: problemconfig: support delete subtask --- .../problemconfig/ProblemConfigEditor.tsx | 6 +-- .../problemconfig/ProblemConfigTree.tsx | 41 +++++++++++++++---- .../problemconfig/reducer/config.ts | 4 ++ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx b/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx index 15df4898f..934e7b074 100644 --- a/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx +++ b/packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx @@ -96,7 +96,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e async componentDidMount() { const { monaco } = await load(['yaml']); const uri = monaco.Uri.parse('hydro://problem/file/config.yaml'); - this.model = monaco.editor.createModel(yaml.dump(configYamlFormat(this.props.config)), 'yaml', uri); + this.model = monaco.editor.createModel(yaml.dump(configYamlFormat(this.props.config), { noArrayIndent: true }), 'yaml', uri); this.vjEditor = Editor.getOrConstruct($(this.containerElement), { language: 'yaml', model: this.model, @@ -112,7 +112,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e componentDidUpdate(prevProps) { if (this.__preventUpdate || !this.model || !this.props.config.__valid) return; - if (yaml.dump(prevProps.config) === yaml.dump(this.props.config)) return; + if (yaml.dump(prevProps.config, { noArrayIndent: true }) === yaml.dump(this.props.config, { noArrayIndent: true })) return; const curValue = this.model.getValue(); const pending = configYamlFormat(this.props.config); try { @@ -120,7 +120,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e if (isEqual(curConfig, pending)) return; } catch { } this.__preventFormat = true; - const diff = diffLines(curValue, yaml.dump(pending)); + const diff = diffLines(curValue, yaml.dump(pending, { noArrayIndent: true })); const ops = []; let cursor = 1; for (const line of diff) { diff --git a/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx b/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx index 873e29b3d..f83547190 100644 --- a/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx +++ b/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx @@ -1,4 +1,5 @@ -import { Icon, TreeNode } from '@blueprintjs/core'; +import { Button, Icon, TreeNode } from '@blueprintjs/core'; +import { Popover2 } from '@blueprintjs/popover2'; import { normalizeSubtasks, readSubtasksFromFiles } from '@hydrooj/utils/lib/common'; import { TestCaseConfig } from 'hydrooj'; import { isEqual } from 'lodash'; @@ -19,9 +20,9 @@ interface TestcasesDndItem { export function SubtaskNode(props: { subtaskId: number }) { const { subtaskId } = props; const subtaskIds = useSelector((s: RootState) => Object.values(s.config?.subtasks || []).map((i) => i.id), isEqual); - const clen = useSelector((state: RootState) => (subtaskId === -1 - ? state.config.__cases.length - : state.config.subtasks.find((i) => i.id === subtaskId).cases?.length || 0)); + const cases = useSelector((state: RootState) => (subtaskId === -1 + ? state.config.__cases + : state.config.subtasks.find((i) => i.id === subtaskId).cases || [])); const time = useSelector((s: RootState) => s.config?.time); const memory = useSelector((s: RootState) => s.config?.memory); const dispatch = useDispatch(); @@ -43,13 +44,37 @@ export function SubtaskNode(props: { subtaskId: number }) { }, })); + function deleteSubtask() { + dispatch({ + type: 'problemconfig/moveTestcases', + payload: { + target: -1, + source: subtaskId, + cases, + }, + }); + dispatch({ + type: 'problemconfig/deleteSubtask', + id: subtaskId, + }); + } + return (
  • {subtaskId !== -1 &&
    setExpand((e) => !e)}>   Subtask {subtaskId} - - + ev.stopPropagation()}> + + Are you sure you want to delete this subtask? +
    + +
    +
    } + > + + }
      @@ -61,10 +86,10 @@ export function SubtaskNode(props: { subtaskId: number }) { id={`s${subtaskId}`} onClick={() => setExpand(false)} icon="layers" - label={<> {clen} testcases.} + label={<> {cases.length} testcases.} path={[0]} />} - {!clen && ( + {!cases.length && (
    • diff --git a/packages/ui-default/components/problemconfig/reducer/config.ts b/packages/ui-default/components/problemconfig/reducer/config.ts index f90d1d7c5..bff419ca7 100644 --- a/packages/ui-default/components/problemconfig/reducer/config.ts +++ b/packages/ui-default/components/problemconfig/reducer/config.ts @@ -125,6 +125,10 @@ export default function reducer(state = { } return { ...state, subtasks, __cases }; } + case 'problemconfig/deleteSubtask': { + const subtasks = state.subtasks.filter((i) => i.id !== action.id); + return { ...state, subtasks }; + } default: return state; }