mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
ui: objective: remember selections & quick navigation
This commit is contained in:
2 files changed
+49
-8
No files matched your search
@@ -2,7 +2,7 @@ import { getScoreColor } from '@hydrooj/utils/lib/status';
|
|||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import Notification from 'vj/components/notification';
|
import Notification from 'vj/components/notification';
|
||||||
import { downloadProblemSet } from 'vj/components/zipDownloader';
|
import { downloadProblemSet } from 'vj/components/zipDownloader';
|
||||||
import { NamedPage } from 'vj/misc/Page';
|
import { NamedPage } from 'vj/misc/Page';
|
||||||
@@ -186,6 +186,7 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
|
|||||||
async function loadObjective() {
|
async function loadObjective() {
|
||||||
$('.outer-loader-container').show();
|
$('.outer-loader-container').show();
|
||||||
const ans = {};
|
const ans = {};
|
||||||
|
const pids = [];
|
||||||
let cnt = 0;
|
let cnt = 0;
|
||||||
const reg = /{{ (input|select|multiselect|textarea)\(\d+(-\d+)?\) }}/g;
|
const reg = /{{ (input|select|multiselect|textarea)\(\d+(-\d+)?\) }}/g;
|
||||||
$('.problem-content .typo').children().each((i, e) => {
|
$('.problem-content .typo').children().each((i, e) => {
|
||||||
@@ -196,15 +197,16 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
|
|||||||
for (const [info, type] of questions) {
|
for (const [info, type] of questions) {
|
||||||
cnt++;
|
cnt++;
|
||||||
const id = info.replace(/{{ (input|select|multiselect|textarea)\((\d+(-\d+)?)\) }}/, '$2');
|
const id = info.replace(/{{ (input|select|multiselect|textarea)\((\d+(-\d+)?)\) }}/, '$2');
|
||||||
|
pids.push(id);
|
||||||
if (type === 'input') {
|
if (type === 'input') {
|
||||||
$(e).html($(e).html().replace(info, tpl`
|
$(e).html($(e).html().replace(info, tpl`
|
||||||
<div class="objective_${id} medium-3" style="display: inline-block;">
|
<div class="objective_${id} medium-3" id="p${id}" style="display: inline-block;">
|
||||||
<input type="text" name="${id}" class="textbox objective-input">
|
<input type="text" name="${id}" class="textbox objective-input">
|
||||||
</div>
|
</div>
|
||||||
`));
|
`));
|
||||||
} else if (type === 'textarea') {
|
} else if (type === 'textarea') {
|
||||||
$(e).html($(e).html().replace(info, tpl`
|
$(e).html($(e).html().replace(info, tpl`
|
||||||
<div class="objective_${id} medium-6">
|
<div class="objective_${id} medium-6" id="p${id}">
|
||||||
<textarea name="${id}" class="textbox objective-input"></textarea>
|
<textarea name="${id}" class="textbox objective-input"></textarea>
|
||||||
</div>
|
</div>
|
||||||
`));
|
`));
|
||||||
@@ -216,7 +218,7 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
|
|||||||
$(e).html($(e).html().replace(info, ''));
|
$(e).html($(e).html().replace(info, ''));
|
||||||
$(e).next('ul').children().each((j, ele) => {
|
$(e).next('ul').children().each((j, ele) => {
|
||||||
$(ele).after(tpl`
|
$(ele).after(tpl`
|
||||||
<label class="objective_${id} radiobox">
|
<label class="objective_${id} radiobox" id="p${id}">
|
||||||
<input type="${type === 'select' ? 'radio' : 'checkbox'}" name="${id}" class="objective-input" value="${String.fromCharCode(65 + j)}">
|
<input type="${type === 'select' ? 'radio' : 'checkbox'}" name="${id}" class="objective-input" value="${String.fromCharCode(65 + j)}">
|
||||||
${String.fromCharCode(65 + j)}. ${{ templateRaw: true, html: ele.innerHTML }}
|
${String.fromCharCode(65 + j)}. ${{ templateRaw: true, html: ele.innerHTML }}
|
||||||
</label>
|
</label>
|
||||||
@@ -226,12 +228,46 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let setUpdate;
|
||||||
|
function ProblemNavigation() {
|
||||||
|
[, setUpdate] = React.useState(0);
|
||||||
|
return <div className="contest-problems" style={{ margin: '1em' }}>
|
||||||
|
{pids.map((i) => <a href={`#p${i}`} className={ans[i] ? 'pass ' : ''}>
|
||||||
|
<span class="id">{i}</span>
|
||||||
|
{ans[i] && <span class="icon icon-check"></span>}
|
||||||
|
</a>)}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAns() {
|
||||||
|
localStorage.setItem(`objective_${UiContext.domain._id}#${UiContext.pdoc.docId}`, JSON.stringify(ans));
|
||||||
|
setUpdate?.((i) => i + 1);
|
||||||
|
}
|
||||||
|
function loadAns() {
|
||||||
|
const saved = localStorage.getItem(`objective_${UiContext.domain._id}#${UiContext.pdoc.docId}`);
|
||||||
|
if (saved) {
|
||||||
|
Object.assign(ans, JSON.parse(saved));
|
||||||
|
for (const [id, val] of Object.entries(ans)) {
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
for (const v of val) {
|
||||||
|
$(`.objective_${id} input[value=${v}]`).prop('checked', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$(`.objective_${id} input[type=text], .objective_${id} textarea`).val(val);
|
||||||
|
$(`.objective_${id}.radiobox [value="${val}"]`).prop('checked', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cnt) {
|
if (cnt) {
|
||||||
|
loadAns();
|
||||||
$('.problem-content .typo').append(document.getElementsByClassName('nav__item--round').length
|
$('.problem-content .typo').append(document.getElementsByClassName('nav__item--round').length
|
||||||
? `<input type="submit" disabled class="button rounded primary disabled" value="${i18n('Login to Submit')}" />`
|
? `<input type="submit" disabled class="button rounded primary disabled" value="${i18n('Login to Submit')}" />`
|
||||||
: `<input type="submit" class="button rounded primary" value="${i18n('Submit')}" />`);
|
: `<input type="submit" class="button rounded primary" value="${i18n('Submit')}" />`);
|
||||||
$('.objective-input[type!=checkbox]').on('input', (e) => {
|
$('.objective-input[type!=checkbox]').on('input', (e) => {
|
||||||
ans[e.target.name] = e.target.value;
|
ans[e.target.name] = e.target.value;
|
||||||
|
saveAns();
|
||||||
});
|
});
|
||||||
$('input.objective-input[type=checkbox]').on('input', (e) => {
|
$('input.objective-input[type=checkbox]').on('input', (e) => {
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
@@ -240,6 +276,7 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
|
|||||||
} else {
|
} else {
|
||||||
ans[e.target.name] = ans[e.target.name].filter((v) => v !== e.target.value);
|
ans[e.target.name] = ans[e.target.name].filter((v) => v !== e.target.value);
|
||||||
}
|
}
|
||||||
|
saveAns();
|
||||||
});
|
});
|
||||||
$('input[type="submit"]').on('click', (e) => {
|
$('input[type="submit"]').on('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -256,8 +293,11 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$('.scratchpad--hide').hide();
|
const ele = document.createElement('div');
|
||||||
|
$(ele).insertBefore($('.scratchpad--hide').get(0));
|
||||||
|
ReactDOM.createRoot(ele).render(<ProblemNavigation />);
|
||||||
$('.non-scratchpad--hide').hide();
|
$('.non-scratchpad--hide').hide();
|
||||||
|
$('.scratchpad--hide').hide();
|
||||||
$('.outer-loader-container').hide();
|
$('.outer-loader-container').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +349,7 @@ const page = new NamedPage(['problem_detail', 'contest_detail_problem', 'homewor
|
|||||||
statusChart.resize();
|
statusChart.resize();
|
||||||
scoreChart.resize();
|
scoreChart.resize();
|
||||||
};
|
};
|
||||||
|
if (UiContext.pdoc.config?.type === 'objective') $($status).hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).on('click', '[name="problem-sidebar__open-scratchpad"]', (ev) => {
|
$(document).on('click', '[name="problem-sidebar__open-scratchpad"]', (ev) => {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
{{ set(UiContext, 'pretestConnUrl', "record-conn?pretest=1&uidOrName=" + handler.user._id + "&pid=" + pdoc.docId + "&domainId=" + handler.args.domainId) }}
|
{{ set(UiContext, 'pretestConnUrl', "record-conn?pretest=1&uidOrName=" + handler.user._id + "&pid=" + pdoc.docId + "&domainId=" + handler.args.domainId) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
<div class="row">
|
<div class="row" data-sticky-parent>
|
||||||
<div class="medium-9 columns">
|
<div class="medium-9 columns">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
@@ -110,10 +110,10 @@
|
|||||||
</div></div>
|
</div></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="medium-3 columns">
|
<div class="medium-3 columns"><div data-sticky="large">
|
||||||
{% set owner_udoc = udoc %}
|
{% set owner_udoc = udoc %}
|
||||||
{% include "partials/problem_sidebar.html" %}
|
{% include "partials/problem_sidebar.html" %}
|
||||||
</div>
|
</div></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:none" class="outer-loader-container"><div class="loader"></div></div>
|
<div style="display:none" class="outer-loader-container"><div class="loader"></div></div>
|
||||||
<div class="scratchpad-container" style="display:none">
|
<div class="scratchpad-container" style="display:none">
|
||||||
|
|||||||
Reference in new issue
Block a user