mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
ui: problemconfig: support delete subtask
This commit is contained in:
3 files changed
+40
-11
No files matched your search
@@ -96,7 +96,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e
|
|||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const { monaco } = await load(['yaml']);
|
const { monaco } = await load(['yaml']);
|
||||||
const uri = monaco.Uri.parse('hydro://problem/file/config.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), {
|
this.vjEditor = Editor.getOrConstruct($(this.containerElement), {
|
||||||
language: 'yaml',
|
language: 'yaml',
|
||||||
model: this.model,
|
model: this.model,
|
||||||
@@ -112,7 +112,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e
|
|||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (this.__preventUpdate || !this.model || !this.props.config.__valid) return;
|
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 curValue = this.model.getValue();
|
||||||
const pending = configYamlFormat(this.props.config);
|
const pending = configYamlFormat(this.props.config);
|
||||||
try {
|
try {
|
||||||
@@ -120,7 +120,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MonacoEditor e
|
|||||||
if (isEqual(curConfig, pending)) return;
|
if (isEqual(curConfig, pending)) return;
|
||||||
} catch { }
|
} catch { }
|
||||||
this.__preventFormat = true;
|
this.__preventFormat = true;
|
||||||
const diff = diffLines(curValue, yaml.dump(pending));
|
const diff = diffLines(curValue, yaml.dump(pending, { noArrayIndent: true }));
|
||||||
const ops = [];
|
const ops = [];
|
||||||
let cursor = 1;
|
let cursor = 1;
|
||||||
for (const line of diff) {
|
for (const line of diff) {
|
||||||
|
|||||||
@@ -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 { normalizeSubtasks, readSubtasksFromFiles } from '@hydrooj/utils/lib/common';
|
||||||
import { TestCaseConfig } from 'hydrooj';
|
import { TestCaseConfig } from 'hydrooj';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
@@ -19,9 +20,9 @@ interface TestcasesDndItem {
|
|||||||
export function SubtaskNode(props: { subtaskId: number }) {
|
export function SubtaskNode(props: { subtaskId: number }) {
|
||||||
const { subtaskId } = props;
|
const { subtaskId } = props;
|
||||||
const subtaskIds = useSelector((s: RootState) => Object.values(s.config?.subtasks || []).map((i) => i.id), isEqual);
|
const subtaskIds = useSelector((s: RootState) => Object.values(s.config?.subtasks || []).map((i) => i.id), isEqual);
|
||||||
const clen = useSelector((state: RootState) => (subtaskId === -1
|
const cases = useSelector((state: RootState) => (subtaskId === -1
|
||||||
? state.config.__cases.length
|
? state.config.__cases
|
||||||
: state.config.subtasks.find((i) => i.id === subtaskId).cases?.length || 0));
|
: state.config.subtasks.find((i) => i.id === subtaskId).cases || []));
|
||||||
const time = useSelector((s: RootState) => s.config?.time);
|
const time = useSelector((s: RootState) => s.config?.time);
|
||||||
const memory = useSelector((s: RootState) => s.config?.memory);
|
const memory = useSelector((s: RootState) => s.config?.memory);
|
||||||
const dispatch = useDispatch();
|
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 (
|
return (
|
||||||
<li className="bp4-tree-node bp4-tree-node-expanded">
|
<li className="bp4-tree-node bp4-tree-node-expanded">
|
||||||
{subtaskId !== -1 && <div className="bp4-tree-node-content" onClick={() => setExpand((e) => !e)}>
|
{subtaskId !== -1 && <div className="bp4-tree-node-content" onClick={() => setExpand((e) => !e)}>
|
||||||
<Icon icon={expand ? 'folder-open' : 'folder-close'} />
|
<Icon icon={expand ? 'folder-open' : 'folder-close'} />
|
||||||
<span className="bp4-tree-node-label">Subtask {subtaskId}</span>
|
<span className="bp4-tree-node-label">Subtask {subtaskId}</span>
|
||||||
<span className="bp4-tree-node-secondary-label">
|
<span className="bp4-tree-node-secondary-label" onClick={(ev) => ev.stopPropagation()}>
|
||||||
<Icon icon="trash"></Icon>
|
<Popover2
|
||||||
|
content={<div style={{ padding: 20 }}>
|
||||||
|
<b>Are you sure you want to delete this subtask?</b>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 15 }}>
|
||||||
|
<Button intent="danger" onClick={deleteSubtask}>Delete</Button>
|
||||||
|
</div>
|
||||||
|
</div>}
|
||||||
|
>
|
||||||
|
<Icon icon="trash" />
|
||||||
|
</Popover2>
|
||||||
</span>
|
</span>
|
||||||
</div>}
|
</div>}
|
||||||
<ul className="bp4-tree-node-list" ref={drop}>
|
<ul className="bp4-tree-node-list" ref={drop}>
|
||||||
@@ -61,10 +86,10 @@ export function SubtaskNode(props: { subtaskId: number }) {
|
|||||||
id={`s${subtaskId}`}
|
id={`s${subtaskId}`}
|
||||||
onClick={() => setExpand(false)}
|
onClick={() => setExpand(false)}
|
||||||
icon="layers"
|
icon="layers"
|
||||||
label={<> {clen} testcases.</>}
|
label={<> {cases.length} testcases.</>}
|
||||||
path={[0]}
|
path={[0]}
|
||||||
/>}
|
/>}
|
||||||
{!clen && (
|
{!cases.length && (
|
||||||
<li className="bp4-tree-node">
|
<li className="bp4-tree-node">
|
||||||
<div className="bp4-tree-node-content">
|
<div className="bp4-tree-node-content">
|
||||||
<span className="bp4-tree-node-caret-none bp4-icon-standard"></span>
|
<span className="bp4-tree-node-caret-none bp4-icon-standard"></span>
|
||||||
|
|||||||
@@ -125,6 +125,10 @@ export default function reducer(state = {
|
|||||||
}
|
}
|
||||||
return { ...state, subtasks, __cases };
|
return { ...state, subtasks, __cases };
|
||||||
}
|
}
|
||||||
|
case 'problemconfig/deleteSubtask': {
|
||||||
|
const subtasks = state.subtasks.filter((i) => i.id !== action.id);
|
||||||
|
return { ...state, subtasks };
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user