From 53e697dfb455a7ec557aad21fa7b14ab3d042eca Mon Sep 17 00:00:00 2001 From: undefined Date: Sun, 19 Mar 2023 04:29:55 +0800 Subject: [PATCH] ui: bug fix --- .../autocomplete/components/AutoComplete.tsx | 19 +++---- .../components/CustomSelectAutoComplete.tsx | 1 - .../components/FileSelectAutoComplete.tsx | 1 - .../problemconfig/ProblemConfigForm.tsx | 2 +- .../problemconfig/ProblemConfigTree.tsx | 2 +- .../problemconfig/reducer/config.ts | 55 ++++++++----------- packages/ui-default/package.json | 2 +- 7 files changed, 33 insertions(+), 49 deletions(-) diff --git a/packages/ui-default/components/autocomplete/components/AutoComplete.tsx b/packages/ui-default/components/autocomplete/components/AutoComplete.tsx index ee0fd0c15..67995379e 100644 --- a/packages/ui-default/components/autocomplete/components/AutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/components/AutoComplete.tsx @@ -34,7 +34,6 @@ export interface AutoCompleteProps { allowEmptyQuery?: boolean; freeSolo?: boolean; freeSoloConverter?: (value: string) => string; - alwaysShowList?: boolean; } export interface AutoCompleteHandle { @@ -83,22 +82,17 @@ function DraggableSelection({ // eslint-disable-next-line prefer-arrow-callback const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, ref: React.Ref>) { - const width = props.width ?? '100%'; - const height = props.height ?? 'auto'; - const disabled = props.disabled ?? false; - const disabledHint = props.disabledHint ?? ''; - const listStyle = props.listStyle ?? {}; + const { + multi = false, width = '100%', height = 'auto', + freeSolo = false, allowEmptyQuery = false, listStyle = {}, + disabled = false, disabledHint = '', draggable = multi, + } = props; const queryItems = props.queryItems ?? (() => []); const renderItem = props.renderItem ?? ((item) => item); const itemText = props.itemText ?? ((item) => item.toString()); const itemKey = props.itemKey ?? itemText; 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 alwaysShowList = props.alwaysShowList ?? false; const [focused, setFocused] = useState(false); // is focused const [selected, setSelected] = useState([]); // selected items @@ -142,6 +136,7 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re useEffect(() => { if (first) first = false; else dispatchChange(); + if (!multi) return; // Load pre-selected items only in multi mode const ids = []; for (const key of selectedKeys) if (!valueCache[key]) ids.push(key); if (!ids.length) return; @@ -154,7 +149,7 @@ const AutoComplete = forwardRef(function Impl(props: AutoCompleteProps, re const handleInputChange = debounce((e?) => queryList(e ? e.target.value : ''), 300); 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) { const idx = selectedKeys.indexOf(key); if (idx !== -1) { diff --git a/packages/ui-default/components/autocomplete/components/CustomSelectAutoComplete.tsx b/packages/ui-default/components/autocomplete/components/CustomSelectAutoComplete.tsx index 76d4f4cb3..9afc9b171 100644 --- a/packages/ui-default/components/autocomplete/components/CustomSelectAutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/components/CustomSelectAutoComplete.tsx @@ -46,7 +46,6 @@ CustomSelectAutoComplete.defaultProps = { selectedKeys: [], freeSolo: false, freeSoloConverter: (input) => input, - alwaysShowList: true, }; export default CustomSelectAutoComplete; diff --git a/packages/ui-default/components/autocomplete/components/FileSelectAutoComplete.tsx b/packages/ui-default/components/autocomplete/components/FileSelectAutoComplete.tsx index 49e34520b..2c13d9055 100644 --- a/packages/ui-default/components/autocomplete/components/FileSelectAutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/components/FileSelectAutoComplete.tsx @@ -46,7 +46,6 @@ FileSelectAutoComplete.defaultProps = { selectedKeys: [], freeSolo: false, freeSoloConverter: (input) => input, - alwaysShowList: true, }; export default FileSelectAutoComplete; diff --git a/packages/ui-default/components/problemconfig/ProblemConfigForm.tsx b/packages/ui-default/components/problemconfig/ProblemConfigForm.tsx index c9c95079e..ef37a4bfa 100644 --- a/packages/ui-default/components/problemconfig/ProblemConfigForm.tsx +++ b/packages/ui-default/components/problemconfig/ProblemConfigForm.tsx @@ -23,7 +23,7 @@ function FileIOConfig() { rightElement={.in/.out} value={filename || ''} onChange={(ev) => { - dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'filename', value: ev.currentTarget.value }); + dispatch({ type: 'problemconfig/updateFileIO', filename: ev.currentTarget.value }); }} fill /> diff --git a/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx b/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx index fcbd219d3..def73b43d 100644 --- a/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx +++ b/packages/ui-default/components/problemconfig/ProblemConfigTree.tsx @@ -135,7 +135,7 @@ export function SubtaskConfigTree() { {ids.map((id) => )}
  • dispatch({ type: 'CONFIG_SUBTASK_UPDATE', id: 0, key: 'add' })} + onClick={() => dispatch({ type: 'problemconfig/addSubtask' })} >
      diff --git a/packages/ui-default/components/problemconfig/reducer/config.ts b/packages/ui-default/components/problemconfig/reducer/config.ts index 4483290ce..9b4846ae4 100644 --- a/packages/ui-default/components/problemconfig/reducer/config.ts +++ b/packages/ui-default/components/problemconfig/reducer/config.ts @@ -15,15 +15,19 @@ const ajv = new Ajv(); const validate = ajv.compile(schema); 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 { switch (action.type) { case 'CONFIG_LOAD_FULFILLED': { + const c = { ...state, __loaded: true }; try { let data = yaml.load(action.payload.config) as any; if (typeof data !== 'object') data = { subtasks: [] }; + else data.subtasks ||= []; 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; for (const subtask of subtasks) { @@ -36,12 +40,12 @@ export default function reducer(state = { } } } - return { - ...state, ...data as any, __valid: true, __errors: [], __loaded: true, - }; + Object.assign(c, data); + c.__valid = true; } catch (e) { - return { ...state, __valid: false, __errors: [e.message] }; + c.__errors.push(e.message); } + return c; } case 'CONFIG_FORM_UPDATE': { const next = { ...state, [action.key]: action.value }; @@ -94,32 +98,10 @@ export default function reducer(state = { } return next; } - case 'CONFIG_SUBTASK_UPDATE': { - const subtasks = cloneDeep(state.subtasks); - const subtask = state.subtasks.find((i) => i.id === action.id); - if (action.value !== '' && ['score', 'id'].includes(action.key)) action.value = +action.value; - 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/updateFileIO': { + const n = { ...state, filename: action.filename }; + if (!n.filename) delete n.filename; + return n; } case 'problemconfig/updateGlobalConfig': { const n = { ...state, time: action.time, memory: action.memory }; @@ -127,6 +109,15 @@ export default function reducer(state = { if (!n.memory) delete n.memory; 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': { if (!state.subtasks.find((i) => i.id === action.id)) return state; const subtask = { ...state.subtasks.find((i) => i.id === action.id) }; diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 56d7bfca0..5372a04c8 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.48.6", + "version": "4.48.7", "author": "undefined ", "license": "AGPL-3.0", "main": "index.ts",