mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
ui: bug fix
This commit is contained in:
7 files changed
+33
-49
No files matched your search
@@ -34,7 +34,6 @@ export interface AutoCompleteProps<Item> {
|
|||||||
allowEmptyQuery?: boolean;
|
allowEmptyQuery?: boolean;
|
||||||
freeSolo?: boolean;
|
freeSolo?: boolean;
|
||||||
freeSoloConverter?: (value: string) => string;
|
freeSoloConverter?: (value: string) => string;
|
||||||
alwaysShowList?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AutoCompleteHandle<Item> {
|
export interface AutoCompleteHandle<Item> {
|
||||||
@@ -83,22 +82,17 @@ function DraggableSelection({
|
|||||||
|
|
||||||
// eslint-disable-next-line prefer-arrow-callback
|
// eslint-disable-next-line prefer-arrow-callback
|
||||||
const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, ref: React.Ref<AutoCompleteHandle<T>>) {
|
const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, ref: React.Ref<AutoCompleteHandle<T>>) {
|
||||||
const width = props.width ?? '100%';
|
const {
|
||||||
const height = props.height ?? 'auto';
|
multi = false, width = '100%', height = 'auto',
|
||||||
const disabled = props.disabled ?? false;
|
freeSolo = false, allowEmptyQuery = false, listStyle = {},
|
||||||
const disabledHint = props.disabledHint ?? '';
|
disabled = false, disabledHint = '', draggable = multi,
|
||||||
const listStyle = props.listStyle ?? {};
|
} = props;
|
||||||
const queryItems = props.queryItems ?? (() => []);
|
const queryItems = props.queryItems ?? (() => []);
|
||||||
const renderItem = props.renderItem ?? ((item) => item);
|
const renderItem = props.renderItem ?? ((item) => item);
|
||||||
const itemText = props.itemText ?? ((item) => item.toString());
|
const itemText = props.itemText ?? ((item) => item.toString());
|
||||||
const itemKey = props.itemKey ?? itemText;
|
const itemKey = props.itemKey ?? itemText;
|
||||||
const onChange = props.onChange ?? (() => { });
|
const onChange = props.onChange ?? (() => { });
|
||||||
const multi = props.multi ?? false;
|
|
||||||
const draggable = props.draggable ?? props.multi;
|
|
||||||
const allowEmptyQuery = props.allowEmptyQuery ?? false;
|
|
||||||
const freeSolo = props.freeSolo ?? false;
|
|
||||||
const freeSoloConverter = freeSolo ? props.freeSoloConverter ?? ((i) => i) : ((i) => i);
|
const freeSoloConverter = freeSolo ? props.freeSoloConverter ?? ((i) => i) : ((i) => i);
|
||||||
const alwaysShowList = props.alwaysShowList ?? false;
|
|
||||||
|
|
||||||
const [focused, setFocused] = useState(false); // is focused
|
const [focused, setFocused] = useState(false); // is focused
|
||||||
const [selected, setSelected] = useState([]); // selected items
|
const [selected, setSelected] = useState([]); // selected items
|
||||||
@@ -142,6 +136,7 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (first) first = false;
|
if (first) first = false;
|
||||||
else dispatchChange();
|
else dispatchChange();
|
||||||
|
if (!multi) return; // Load pre-selected items only in multi mode
|
||||||
const ids = [];
|
const ids = [];
|
||||||
for (const key of selectedKeys) if (!valueCache[key]) ids.push(key);
|
for (const key of selectedKeys) if (!valueCache[key]) ids.push(key);
|
||||||
if (!ids.length) return;
|
if (!ids.length) return;
|
||||||
@@ -154,7 +149,7 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
|||||||
const handleInputChange = debounce((e?) => queryList(e ? e.target.value : ''), 300);
|
const handleInputChange = debounce((e?) => queryList(e ? e.target.value : ''), 300);
|
||||||
|
|
||||||
const toggleItem = (item: T, key = itemKey(item), preserve = false) => {
|
const toggleItem = (item: T, key = itemKey(item), preserve = false) => {
|
||||||
const shouldKeepOpen = (multi ? alwaysShowList : allowEmptyQuery) && inputRef.current.value === '';
|
const shouldKeepOpen = multi && allowEmptyQuery && inputRef.current.value === '';
|
||||||
if (multi) {
|
if (multi) {
|
||||||
const idx = selectedKeys.indexOf(key);
|
const idx = selectedKeys.indexOf(key);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ CustomSelectAutoComplete.defaultProps = {
|
|||||||
selectedKeys: [],
|
selectedKeys: [],
|
||||||
freeSolo: false,
|
freeSolo: false,
|
||||||
freeSoloConverter: (input) => input,
|
freeSoloConverter: (input) => input,
|
||||||
alwaysShowList: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CustomSelectAutoComplete;
|
export default CustomSelectAutoComplete;
|
||||||
@@ -46,7 +46,6 @@ FileSelectAutoComplete.defaultProps = {
|
|||||||
selectedKeys: [],
|
selectedKeys: [],
|
||||||
freeSolo: false,
|
freeSolo: false,
|
||||||
freeSoloConverter: (input) => input,
|
freeSoloConverter: (input) => input,
|
||||||
alwaysShowList: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FileSelectAutoComplete;
|
export default FileSelectAutoComplete;
|
||||||
@@ -23,7 +23,7 @@ function FileIOConfig() {
|
|||||||
rightElement={<Tag minimal>.in/.out</Tag>}
|
rightElement={<Tag minimal>.in/.out</Tag>}
|
||||||
value={filename || ''}
|
value={filename || ''}
|
||||||
onChange={(ev) => {
|
onChange={(ev) => {
|
||||||
dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'filename', value: ev.currentTarget.value });
|
dispatch({ type: 'problemconfig/updateFileIO', filename: ev.currentTarget.value });
|
||||||
}}
|
}}
|
||||||
fill
|
fill
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ export function SubtaskConfigTree() {
|
|||||||
{ids.map((id) => <SubtaskNode key={id} subtaskId={id} />)}
|
{ids.map((id) => <SubtaskNode key={id} subtaskId={id} />)}
|
||||||
<li
|
<li
|
||||||
className="bp4-tree-node"
|
className="bp4-tree-node"
|
||||||
onClick={() => dispatch({ type: 'CONFIG_SUBTASK_UPDATE', id: 0, key: 'add' })}
|
onClick={() => dispatch({ type: 'problemconfig/addSubtask' })}
|
||||||
>
|
>
|
||||||
<div className="bp4-tree-node-content bp4-tree-node-content-0">
|
<div className="bp4-tree-node-content bp4-tree-node-content-0">
|
||||||
<Icon icon="folder-new" />
|
<Icon icon="folder-new" />
|
||||||
|
|||||||
@@ -15,15 +15,19 @@ const ajv = new Ajv();
|
|||||||
const validate = ajv.compile(schema);
|
const validate = ajv.compile(schema);
|
||||||
|
|
||||||
export default function reducer(state = {
|
export default function reducer(state = {
|
||||||
type: 'default', __loaded: false, __valid: true, __errors: [], __cases: [], subtasks: [],
|
type: 'default', __loaded: false, __valid: false, __errors: [], __cases: [], subtasks: [],
|
||||||
} as State, action: any = {}): State {
|
} as State, action: any = {}): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'CONFIG_LOAD_FULFILLED': {
|
case 'CONFIG_LOAD_FULFILLED': {
|
||||||
|
const c = { ...state, __loaded: true };
|
||||||
try {
|
try {
|
||||||
let data = yaml.load(action.payload.config) as any;
|
let data = yaml.load(action.payload.config) as any;
|
||||||
if (typeof data !== 'object') data = { subtasks: [] };
|
if (typeof data !== 'object') data = { subtasks: [] };
|
||||||
|
else data.subtasks ||= [];
|
||||||
if (!validate(data)) {
|
if (!validate(data)) {
|
||||||
return { ...state, __valid: false, __errors: validate.errors.map((i) => `${i.instancePath}: ${i.message}`) };
|
Object.assign(c, data);
|
||||||
|
c.__errors = validate.errors.map((i) => `${i.instancePath}: ${i.message}`);
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
const subtasks = (data as any).subtasks;
|
const subtasks = (data as any).subtasks;
|
||||||
for (const subtask of subtasks) {
|
for (const subtask of subtasks) {
|
||||||
@@ -36,12 +40,12 @@ export default function reducer(state = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
Object.assign(c, data);
|
||||||
...state, ...data as any, __valid: true, __errors: [], __loaded: true,
|
c.__valid = true;
|
||||||
};
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { ...state, __valid: false, __errors: [e.message] };
|
c.__errors.push(e.message);
|
||||||
}
|
}
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
case 'CONFIG_FORM_UPDATE': {
|
case 'CONFIG_FORM_UPDATE': {
|
||||||
const next = { ...state, [action.key]: action.value };
|
const next = { ...state, [action.key]: action.value };
|
||||||
@@ -94,32 +98,10 @@ export default function reducer(state = {
|
|||||||
}
|
}
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
case 'CONFIG_SUBTASK_UPDATE': {
|
case 'problemconfig/updateFileIO': {
|
||||||
const subtasks = cloneDeep(state.subtasks);
|
const n = { ...state, filename: action.filename };
|
||||||
const subtask = state.subtasks.find((i) => i.id === action.id);
|
if (!n.filename) delete n.filename;
|
||||||
if (action.value !== '' && ['score', 'id'].includes(action.key)) action.value = +action.value;
|
return n;
|
||||||
if (action.key === 'if' && action.value.join('') !== '') action.value = action.value.map((i) => +i);
|
|
||||||
if (action.key.split('-')[0] === 'cases') {
|
|
||||||
if (action.key === 'cases-add') subtask.cases.push(action.value);
|
|
||||||
else if (action.key === 'cases-edit') {
|
|
||||||
if (action.value === '' && !['input', 'output'].includes(action.casesKey)) delete subtask.cases[action.casesId][action.casesKey];
|
|
||||||
else subtask.cases[action.casesId][action.casesKey] = action.value;
|
|
||||||
} else if (action.key === 'cases-delete') {
|
|
||||||
subtask.cases = subtask.cases.filter((k, v) => v !== action.value);
|
|
||||||
}
|
|
||||||
} else if (action.key === 'add') {
|
|
||||||
subtasks.push({
|
|
||||||
cases: [],
|
|
||||||
score: 0,
|
|
||||||
id: Object.keys(subtasks).map((i) => subtasks[i].id).reduce((a, b) => Math.max(+a, +b), 0) + 1,
|
|
||||||
});
|
|
||||||
return { ...state, subtasks };
|
|
||||||
} else if (action.key === 'delete') return { ...state, subtasks: subtasks.filter((k, v) => v !== action.id) };
|
|
||||||
else {
|
|
||||||
if (action.value === '' || (action.key === 'if' && action.value.join('') === '')) delete subtask[action.key];
|
|
||||||
else subtask[action.key] = action.value;
|
|
||||||
}
|
|
||||||
return { ...state, subtasks };
|
|
||||||
}
|
}
|
||||||
case 'problemconfig/updateGlobalConfig': {
|
case 'problemconfig/updateGlobalConfig': {
|
||||||
const n = { ...state, time: action.time, memory: action.memory };
|
const n = { ...state, time: action.time, memory: action.memory };
|
||||||
@@ -127,6 +109,15 @@ export default function reducer(state = {
|
|||||||
if (!n.memory) delete n.memory;
|
if (!n.memory) delete n.memory;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
case 'problemconfig/addSubtask': {
|
||||||
|
const subtasks = cloneDeep(state.subtasks);
|
||||||
|
subtasks.push({
|
||||||
|
cases: [],
|
||||||
|
score: 0,
|
||||||
|
id: Object.keys(subtasks).map((i) => subtasks[i].id).reduce((a, b) => Math.max(+a, +b), 0) + 1,
|
||||||
|
});
|
||||||
|
return { ...state, subtasks };
|
||||||
|
}
|
||||||
case 'problemconfig/updateSubtaskConfig': {
|
case 'problemconfig/updateSubtaskConfig': {
|
||||||
if (!state.subtasks.find((i) => i.id === action.id)) return state;
|
if (!state.subtasks.find((i) => i.id === action.id)) return state;
|
||||||
const subtask = { ...state.subtasks.find((i) => i.id === action.id) };
|
const subtask = { ...state.subtasks.find((i) => i.id === action.id) };
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/ui-default",
|
"name": "@hydrooj/ui-default",
|
||||||
"version": "4.48.6",
|
"version": "4.48.7",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
|
|||||||
Reference in new issue
Block a user