1
1
import type { editor } from 'monaco-editor' ;
2
2
import dayjs from 'dayjs' ;
3
3
import { useCallback , useContext , useEffect , useMemo , useState } from 'react' ;
4
- import { Button , Input , Skeleton } from '@arco-design/web-react' ;
4
+ import {
5
+ Button ,
6
+ Input ,
7
+ Modal ,
8
+ Skeleton ,
9
+ TabsProps ,
10
+ Tooltip ,
11
+ } from '@arco-design/web-react' ;
5
12
import debounce from 'lodash.debounce' ;
13
+ import { IconUndo } from '@arco-design/web-react/icon' ;
6
14
import { CustomTabs } from '@src/components/CustomTabs' ;
7
15
import localCache , { PROBLEM_STATUS } from '@src/utils/local-cache' ;
8
16
import emitter from '@src/utils/emit' ;
@@ -20,6 +28,7 @@ import {
20
28
monacoInstance ,
21
29
validateMonacoModel ,
22
30
} from '@src/utils/monaco' ;
31
+ import { Setting } from '@src/utils/setting' ;
23
32
import styles from './index.module.less' ;
24
33
25
34
const enum MainTab {
@@ -35,8 +44,65 @@ function formatErrorFromMarkers(markers: editor.IMarker[]) {
35
44
} ) ;
36
45
}
37
46
47
+ function createResultError ( status : string [ ] ) {
48
+ return (
49
+ < div className = { styles [ 'result-errors' ] } >
50
+ < div className = { styles [ 'result-error-title' ] } > Compilation Error</ div >
51
+ < div className = { styles [ 'result-error-info' ] } >
52
+ { status . map ( function ( error ) {
53
+ return (
54
+ < div key = { error } className = { styles [ 'result-error-item' ] } >
55
+ { error }
56
+ </ div >
57
+ ) ;
58
+ } ) }
59
+ </ div >
60
+ </ div >
61
+ ) ;
62
+ }
63
+
64
+ function createCasesError (
65
+ cases : NonNullable < Problem [ 'cases' ] > ,
66
+ casesErrors : string [ ] [ ] ,
67
+ language : Setting [ 'language' ] ,
68
+ ) {
69
+ return (
70
+ < CustomTabs className = { styles [ 'case-tabs' ] } >
71
+ { cases . map ( function ( _ , index ) {
72
+ const result = casesErrors [ index ] ;
73
+ return (
74
+ < CustomTabs . TabPane
75
+ key = { index }
76
+ title = { `${ i18nJson [ 'case' ] [ language ] } ${ index + 1 } ` }
77
+ >
78
+ { result . length > 0 && (
79
+ < div className = { styles [ 'result-error-info' ] } >
80
+ { result . map ( function ( error ) {
81
+ return (
82
+ < div key = { error } className = { styles [ 'result-error-item' ] } >
83
+ { error }
84
+ </ div >
85
+ ) ;
86
+ } ) }
87
+ </ div >
88
+ ) }
89
+ { result . length === 0 && (
90
+ < div className = { styles [ 'result-pass' ] } > Pass!</ div >
91
+ ) }
92
+ </ CustomTabs . TabPane >
93
+ ) ;
94
+ } ) }
95
+ </ CustomTabs >
96
+ ) ;
97
+ }
98
+
38
99
const Results = function ( ) {
39
- const [ { currentProblem, setting } ] = useContext ( Context ) ;
100
+ const [
101
+ {
102
+ currentProblem,
103
+ setting : { language } ,
104
+ } ,
105
+ ] = useContext ( Context ) ;
40
106
const [ loading , setLoading ] = useState ( true ) ;
41
107
const [ activeMainTab , setActiveMainTab ] = useState < string > ( MainTab . cases ) ;
42
108
const [ status , setStatus ] = useState < string [ ] | 'Accept!' > ( [ ] ) ;
@@ -46,6 +112,7 @@ const Results = function () {
46
112
const [ testRaw , setTestRaw ] = useState < string | undefined > ( undefined ) ;
47
113
const [ casesErrors , setCasesErrors ] = useState < string [ ] [ ] > ( [ ] ) ;
48
114
const [ btnDisabled , setBtnDisabled ] = useState ( true ) ;
115
+ const [ activeCase , setActiveCase ] = useState < string > ( '0' ) ;
49
116
50
117
const monacoEditorStatusListener = useCallback (
51
118
( ) => setBtnDisabled ( false ) ,
@@ -58,6 +125,7 @@ const Results = function () {
58
125
setTestRaw ( raw ) ;
59
126
setStatus ( [ ] ) ;
60
127
setCasesErrors ( [ ] ) ;
128
+ setActiveCase ( '0' ) ;
61
129
setCases ( problem . cases || [ NULL_CASE ] ) ;
62
130
setActiveMainTab ( MainTab . cases ) ;
63
131
setLoading ( false ) ;
@@ -170,65 +238,93 @@ const Results = function () {
170
238
if ( typeof status === 'string' ) {
171
239
return < div className = { styles [ 'result-accept' ] } > Accepted!</ div > ;
172
240
} else if ( Array . isArray ( status ) && status . length > 0 ) {
173
- return (
174
- < div className = { styles [ 'result-errors' ] } >
175
- < div className = { styles [ 'result-error-title' ] } >
176
- Compilation Error
177
- </ div >
178
- < div className = { styles [ 'result-error-info' ] } >
179
- { status . map ( function ( error ) {
180
- return (
181
- < div key = { error } className = { styles [ 'result-error-item' ] } >
182
- { error }
183
- </ div >
184
- ) ;
185
- } ) }
186
- </ div >
187
- </ div >
188
- ) ;
241
+ return createResultError ( status ) ;
189
242
} else if ( casesErrors . length > 0 ) {
190
- return (
191
- < CustomTabs className = { styles [ 'case-tabs' ] } >
192
- { cases . map ( function ( _ , index ) {
193
- const result = casesErrors [ index ] ;
194
- return (
195
- < CustomTabs . TabPane
196
- key = { index }
197
- title = { `${ i18nJson [ 'case' ] [ setting . language ] } ${ index + 1 } ` }
198
- >
199
- { result . length > 0 && (
200
- < div className = { styles [ 'result-error-info' ] } >
201
- { result . map ( function ( error ) {
202
- return (
203
- < div
204
- key = { error }
205
- className = { styles [ 'result-error-item' ] }
206
- >
207
- { error }
208
- </ div >
209
- ) ;
210
- } ) }
211
- </ div >
212
- ) }
213
- { result . length === 0 && (
214
- < div className = { styles [ 'result-pass' ] } > Pass!</ div >
215
- ) }
216
- </ CustomTabs . TabPane >
217
- ) ;
218
- } ) }
219
- </ CustomTabs >
220
- ) ;
243
+ return createCasesError ( cases , casesErrors , language ) ;
221
244
} else {
222
245
return (
223
246
< div className = { styles [ 'result-empty' ] } >
224
- { i18nJson [ 'please_run_or_submit_first' ] [ setting . language ] }
247
+ { i18nJson [ 'please_run_or_submit_first' ] [ language ] }
225
248
</ div >
226
249
) ;
227
250
}
228
251
} ,
229
252
[ cases , casesErrors , status ] ,
230
253
) ;
231
254
255
+ function onAddCase ( ) {
256
+ if ( cases . length >= 5 ) return ;
257
+ setActiveCase ( String ( cases . length ) ) ;
258
+ setCases ( [ ...cases , NULL_CASE ] ) ;
259
+ }
260
+
261
+ function onDeleteCase ( key : string ) {
262
+ if ( cases . length <= 1 ) return ;
263
+ if ( String ( cases . length - 1 ) === activeCase ) {
264
+ setActiveCase ( String ( cases . length - 2 ) ) ;
265
+ }
266
+ setCases ( cases . filter ( ( _ , index ) => String ( index ) !== key ) ) ;
267
+ }
268
+
269
+ function onChangeCase (
270
+ key : number ,
271
+ newCase : Partial < NonNullable < Problem [ 'cases' ] > [ number ] > ,
272
+ ) {
273
+ setCases (
274
+ cases . map ( function ( originCase , index ) {
275
+ if ( key === index ) {
276
+ return {
277
+ ...originCase ,
278
+ ...newCase ,
279
+ } ;
280
+ }
281
+ return originCase ;
282
+ } ) ,
283
+ ) ;
284
+ }
285
+
286
+ function resetCases ( ) {
287
+ const modal = Modal . confirm ( {
288
+ title : i18nJson [ 'confirm_title' ] [ language ] ,
289
+ content : i18nJson [ 'confirm_reset_cases' ] [ language ] ,
290
+ okText : i18nJson [ 'confirm_btn' ] [ language ] ,
291
+ cancelText : i18nJson [ 'cancel_btn' ] [ language ] ,
292
+ onOk : async function ( ) {
293
+ setActiveCase ( '0' ) ;
294
+ setCases ( originCases . length == 0 ? [ NULL_CASE ] : originCases ) ;
295
+ modal . close ( ) ;
296
+ } ,
297
+ } ) ;
298
+ }
299
+
300
+ const renderTabHeader : TabsProps [ 'renderTabHeader' ] = function (
301
+ tabProps ,
302
+ DefaultTabHeader ,
303
+ ) {
304
+ if ( noCases ) {
305
+ return < DefaultTabHeader { ...tabProps } /> ;
306
+ }
307
+ return (
308
+ < div
309
+ style = { {
310
+ width : '100%' ,
311
+ display : 'flex' ,
312
+ alignItems : 'center' ,
313
+ justifyContent : 'space-between' ,
314
+ } }
315
+ >
316
+ < div style = { { flex : 1 } } >
317
+ < DefaultTabHeader { ...tabProps } />
318
+ </ div >
319
+ < a onClick = { resetCases } >
320
+ < Tooltip mini = { true } content = { 'reset' } >
321
+ < IconUndo />
322
+ </ Tooltip >
323
+ </ a >
324
+ </ div >
325
+ ) ;
326
+ } ;
327
+
232
328
return (
233
329
< Skeleton
234
330
loading = { loading }
@@ -244,28 +340,42 @@ const Results = function () {
244
340
>
245
341
< CustomTabs . TabPane
246
342
key = { MainTab . cases }
247
- title = { i18nJson [ MainTab . cases ] [ setting . language ] }
343
+ title = { i18nJson [ MainTab . cases ] [ language ] }
248
344
>
249
- < CustomTabs className = { styles [ 'case-tabs' ] } >
345
+ < CustomTabs
346
+ editable = { ! noCases }
347
+ onAddTab = { onAddCase }
348
+ onDeleteTab = { onDeleteCase }
349
+ activeTab = { activeCase }
350
+ onChange = { setActiveCase }
351
+ className = { styles [ 'case-tabs' ] }
352
+ renderTabHeader = { renderTabHeader }
353
+ >
250
354
{ cases . map ( function ( { source, target } , index ) {
251
355
return (
252
356
< CustomTabs . TabPane
253
357
key = { index }
254
- title = { `${ i18nJson [ 'case' ] [ setting . language ] } ${ index + 1 } ` }
358
+ title = { `${ i18nJson [ 'case' ] [ language ] } ${ index + 1 } ` }
255
359
>
256
360
< div className = { styles [ 'case-header' ] } > Source</ div >
257
361
< Input . TextArea
258
362
value = { source }
259
363
autoSize = { true }
260
364
readOnly = { noCases }
261
365
className = { styles [ 'case-input' ] }
366
+ onChange = { newSource =>
367
+ onChangeCase ( index , { source : newSource } )
368
+ }
262
369
/>
263
370
< div className = { styles [ 'case-header' ] } > Target</ div >
264
371
< Input . TextArea
265
372
value = { target }
266
373
autoSize = { true }
267
374
readOnly = { noCases }
268
375
className = { styles [ 'case-input' ] }
376
+ onChange = { newTarget =>
377
+ onChangeCase ( index , { target : newTarget } )
378
+ }
269
379
/>
270
380
</ CustomTabs . TabPane >
271
381
) ;
@@ -274,7 +384,7 @@ const Results = function () {
274
384
</ CustomTabs . TabPane >
275
385
< CustomTabs . TabPane
276
386
key = { MainTab . result }
277
- title = { i18nJson [ MainTab . result ] [ setting . language ] }
387
+ title = { i18nJson [ MainTab . result ] [ language ] }
278
388
>
279
389
{ resultContent }
280
390
</ CustomTabs . TabPane >
@@ -286,7 +396,7 @@ const Results = function () {
286
396
disabled = { btnDisabled }
287
397
className = { styles . btn }
288
398
>
289
- { i18nJson [ 'run_code' ] [ setting . language ] }
399
+ { i18nJson [ 'run_code' ] [ language ] }
290
400
</ Button >
291
401
< Button
292
402
type = { 'primary' }
@@ -295,7 +405,7 @@ const Results = function () {
295
405
disabled = { btnDisabled }
296
406
className = { styles . btn }
297
407
>
298
- { i18nJson [ 'submit_code' ] [ setting . language ] }
408
+ { i18nJson [ 'submit_code' ] [ language ] }
299
409
</ Button >
300
410
</ div >
301
411
</ div >
0 commit comments