ui: bug fixes

This commit is contained in:
undefined committed 2023-03-17 16:27:12 +08:00
1 parent b355a869b4
commit e78925c082
15 files changed
+76 -54

No files matched your search

@@ -34,6 +34,7 @@ export interface AutoCompleteProps<Item> {
allowEmptyQuery?: boolean;
freeSolo?: boolean;
freeSoloConverter?: (value: string) => string;
alwaysShowList?: boolean;
}
export interface AutoCompleteHandle<Item> {
@@ -97,6 +98,7 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
const allowEmptyQuery = props.allowEmptyQuery ?? false;
const freeSolo = props.freeSolo ?? false;
const freeSoloConverter = freeSolo ? props.freeSoloConverter ?? ((i) => i) : ((i) => i);
const alwaysShowList = props.alwaysShowList ?? false;
const [focused, setFocused] = useState(false); // is focused
const [selected, setSelected] = useState([]); // selected items
@@ -152,7 +154,7 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
const handleInputChange = debounce((e?) => queryList(e ? e.target.value : ''), 300);
const toggleItem = (item: T, key = itemKey(item), preserve = false) => {
const shouldKeepOpen = allowEmptyQuery && inputRef.current.value === '' && !multi;
const shouldKeepOpen = (multi ? alwaysShowList : allowEmptyQuery) && inputRef.current.value === '';
if (multi) {
const idx = selectedKeys.indexOf(key);
if (idx !== -1) {
@@ -46,6 +46,7 @@ CustomSelectAutoComplete.defaultProps = {
selectedKeys: [],
freeSolo: false,
freeSoloConverter: (input) => input,
alwaysShowList: true,
};
export default CustomSelectAutoComplete;
@@ -46,6 +46,7 @@ FileSelectAutoComplete.defaultProps = {
selectedKeys: [],
freeSolo: false,
freeSoloConverter: (input) => input,
alwaysShowList: true,
};
export default FileSelectAutoComplete;
@@ -85,7 +85,7 @@ function LangConfig() {
<CustomSelectAutoComplete
ref={ref}
data={data}
placeholder={i18n('Unlimited')}
placeholder={!selectedKeys.length ? i18n('Unlimited') : i18n('Code language')}
selectedKeys={selectedKeys}
onChange={(val) => {
const value = val.split(',');
@@ -79,7 +79,7 @@ export function SubtaskNode(props: { subtaskId: number }) {
</span>
</div>}
<ul className="bp4-tree-node-list" ref={drop}>
{subtaskId !== -1 && <SubtaskSettings subtaskId={subtaskId} time={time} memory={memory} />}
{subtaskId !== -1 && <SubtaskSettings subtaskId={subtaskId} subtaskIds={subtaskIds} time={time} memory={memory} />}
{expand
? <SelectionManager subtaskId={subtaskId} subtaskIds={subtaskIds} />
: <TreeNode
@@ -15,17 +15,18 @@ const ajv = new Ajv();
const validate = ajv.compile(schema);
export default function reducer(state = {
type: 'default', __loaded: false, __valid: true, __errors: [], __cases: [],
type: 'default', __loaded: false, __valid: true, __errors: [], __cases: [], subtasks: [],
} as State, action: any = {}): State {
switch (action.type) {
case 'CONFIG_LOAD_FULFILLED': {
try {
const data = yaml.load(action.payload.config) as any;
let data = yaml.load(action.payload.config) as any;
if (typeof data !== 'object') data = { subtasks: [] };
if (!validate(data)) {
return { ...state, __valid: false, __errors: validate.errors.map((i) => `${i.instancePath}: ${i.message}`) };
}
const subtasks = (data as any).subtasks;
for (const subtask of subtasks || []) {
for (const subtask of subtasks) {
if (typeof subtask.id !== 'number') {
for (let i = 1; ; i++) {
if (!subtasks.find((s) => s.id === i)) {
@@ -13,31 +13,47 @@ export function AddTestcase() {
const [input, setInput] = React.useState('');
const [output, setOutput] = React.useState('');
const [valid, setValid] = React.useState(false);
const [autoInput, setAutoInput] = React.useState(true);
const [autoOutput, setAutoOutput] = React.useState(true);
const testdata = useSelector((state: RootState) => state.testdata);
const store = useStore<RootState>();
const refInput = useRef();
const refOutput = useRef();
useEffect(() => {
setValid(testdata.find((i) => i.name === input) && testdata.find((i) => i.name === output));
if (input && !output) {
const inputValid = testdata.find((i) => i.name === input);
if (input) setAutoInput(false);
else setAutoInput(true);
setValid(inputValid);
if (inputValid && autoOutput) {
const filename = input.substring(0, input.lastIndexOf('.'));
let outputFile = '';
if (testdata.find((i) => i.name === `${filename}.out`)) outputFile = `${filename}.out`;
else if (testdata.find((i) => i.name === `${filename}.ans`)) outputFile = `${filename}.ans`;
// @ts-ignore
refOutput.current!.setSelectedItems([outputFile]);
setOutput(outputFile);
if (outputFile) {
// @ts-ignore
refOutput.current!.setSelectedItems([outputFile]);
setOutput(outputFile);
setAutoOutput(true);
}
}
if (output && !input) {
}, [input]);
useEffect(() => {
const outputValid = testdata.find((i) => i.name === output);
if (output) setAutoOutput(false);
else setAutoOutput(true);
setValid(outputValid);
if (outputValid && autoInput) {
const filename = output.substring(0, output.lastIndexOf('.'));
if (testdata.find((i) => i.name === `${filename}.in`)) {
// @ts-ignore
refInput.current!.setSelectedItems([`${filename}.in`]);
setInput(`${filename}.in`);
setAutoInput(true);
}
}
}, [input, output]);
}, [output]);
function onConfirm() {
store.dispatch({
@@ -81,6 +97,8 @@ export function AddTestcase() {
onClick={() => {
setInput('');
setOutput('');
setAutoInput(true);
setAutoOutput(true);
setOpen(true);
}}
>
@@ -8,9 +8,11 @@ import { isEqual } from 'lodash';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { i18n } from 'vj/utils';
import CustomSelectAutoComplete from '../../autocomplete/components/CustomSelectAutoComplete';
import { RootState } from '../reducer';
interface SubtaskSettingsProps {
subtaskIds: number[];
subtaskId: number;
time: string;
memory: string;
@@ -19,6 +21,7 @@ interface SubtaskSettingsProps {
export function SubtaskSettings(props: SubtaskSettingsProps) {
const [open, setOpen] = React.useState(false);
const [depsOpen, setDepsOpen] = React.useState(false);
const subtaskIds = props.subtaskIds.filter((i) => i !== props.subtaskId);
const score = useSelector((state: RootState) => state.config.subtasks.find((i) => i.id === props.subtaskId).score);
const time = useSelector((state: RootState) => state.config.subtasks.find((i) => i.id === props.subtaskId).time);
const memory = useSelector((state: RootState) => state.config.subtasks.find((i) => i.id === props.subtaskId).memory);
@@ -88,14 +91,13 @@ export function SubtaskSettings(props: SubtaskSettingsProps) {
</Dialog>
<Dialog title={i18n('Set dependencies')} icon="cog" isOpen={depsOpen} onClose={() => setDepsOpen(false)}>
<DialogBody>
<ControlGroup fill={true} vertical={false}>
<InputGroup
leftElement={<Icon icon="diagram-tree" />}
onChange={(ev) => setDeps(ev.currentTarget.value)}
placeholder={'Dependencies'}
value={cdeps || ''}
/>
</ControlGroup>
<CustomSelectAutoComplete
data={subtaskIds.map((i) => ({ _id: i, name: `${i18n('Subtask {0}', i)}` }))}
setSelectItems={cdeps.split(',').map((i) => i.trim()).filter((i) => +i).map((i) => +i)}
onChange={(items) => setDeps(items)}
placeholder="dependencies"
multi
/>
</DialogBody>
<DialogFooter actions={<Button className="primary rounded button" onClick={onConfirm} intent="primary" text="Save" />} />
</Dialog>
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/ui-default",
"version": "4.48.5",
"version": "4.48.6",
"author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0",
"main": "index.ts",
@@ -25,7 +25,7 @@
"@fontsource/source-code-pro": "^4.5.14",
"@fontsource/ubuntu-mono": "^4.5.11",
"@hydrooj/utils": "workspace:*",
"@simplewebauthn/browser": "^7.1.0",
"@simplewebauthn/browser": "^7.2.0",
"@svgr/webpack": "^6.5.1",
"@types/gulp-if": "^0.0.34",
"@types/jquery": "^3.5.16",
@@ -37,8 +37,8 @@
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@types/redux-logger": "^3.0.9",
"@types/serviceworker": "^0.0.65",
"@types/sharedworker": "^0.0.94",
"@types/serviceworker": "^0.0.66",
"@types/sharedworker": "^0.0.95",
"@types/webpack-env": "^1.18.0",
"@vscode/codicons": "^0.0.32",
"ajv": "^8.12.0",
@@ -29,6 +29,9 @@
.button
margin: 0
input::placeholder
color: rgba(95,107,124,.6)!important
.bp4-input-group
input
appearance: none
@@ -46,6 +49,12 @@
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1)
box-shadow: none
.bp4-tag.bp4-minimal
background: transparent
.bp4-input-action
pointer-events: none
.problem-config-form
font-family: var(--code-font-family)
@@ -54,25 +63,6 @@
input[type="number"]
-moz-appearance: textfield
.bp4-tag.bp4-minimal
background: transparent
.bp4-input-action
pointer-events: none
.data-table
tr
border-top: 0
border-bottom: 0
height: auto
td
padding: 0
.thead td
text-align: center
td input, td div
background: transparent
.hover-row.status-bar
display: none
input::placeholder
color: rgba(95,107,124,.6)!important