|
12 | 12 | } from 'svelte-5-ui-lib'; |
13 | 13 |
|
14 | 14 | import type { TaskResults, TaskResult } from '$lib/types/task'; |
15 | | - import { ContestType } from '$lib/types/contest'; |
| 15 | + import type { ContestTableProvider } from '$lib/types/contest_table_provider'; |
16 | 16 |
|
17 | 17 | import UpdatingModal from '$lib/components/SubmissionStatus/UpdatingModal.svelte'; |
18 | 18 | import TaskTableBodyCell from '$lib/components/TaskTables/TaskTableBodyCell.svelte'; |
19 | 19 |
|
20 | 20 | import { |
21 | | - type TaskResultsFilter, |
22 | | - taskResultsForABCLatest20, |
23 | | - taskResultsFromABC319Onwards, |
24 | | - taskResultsFromABC212ToABC318, |
25 | | - } from '$lib/utils/task_results_filter'; |
26 | | -
|
27 | | - import { |
28 | | - type TaskTableGenerator, |
29 | | - taskTableGeneratorForABCLatest20, |
30 | | - taskTableGeneratorFromABC319Onwards, |
31 | | - taskTableGeneratorFromABC212ToABC318, |
32 | | - } from '$lib/utils/task_table_generator'; |
| 21 | + contestTableProviders, |
| 22 | + type ContestTableProviders, |
| 23 | + } from '$lib/utils/contest_table_provider'; |
33 | 24 |
|
34 | 25 | import { getBackgroundColorFrom } from '$lib/services/submission_status'; |
35 | 26 |
|
|
40 | 31 |
|
41 | 32 | let { taskResults, isLoggedIn }: Props = $props(); |
42 | 33 |
|
43 | | - // TODO: 任意のコンテスト種別を追加 |
44 | | - // TODO: コンテスト種別の並び順を決める |
45 | | - const contestFilterConfigs = { |
46 | | - abcLatest20Rounds: { |
47 | | - filter: () => taskResultsForABCLatest20(taskResults), |
48 | | - table: (results: TaskResults) => taskTableGeneratorForABCLatest20(results, ContestType.ABC), |
49 | | - buttonLabel: 'ABC 最新 20 回', |
50 | | - ariaLabel: 'Filter ABC latest 20 rounds', |
51 | | - }, |
52 | | - abc319Onwards: { |
53 | | - filter: () => taskResultsFromABC319Onwards(taskResults), |
54 | | - table: (results: TaskResults) => |
55 | | - taskTableGeneratorFromABC319Onwards(results, ContestType.ABC), |
56 | | - buttonLabel: 'ABC319 〜', |
57 | | - ariaLabel: 'Filter contests from ABC 319 onwards', |
58 | | - }, |
59 | | - abc212To318: { |
60 | | - filter: () => taskResultsFromABC212ToABC318(taskResults), |
61 | | - table: (results: TaskResults) => |
62 | | - taskTableGeneratorFromABC212ToABC318(results, ContestType.ABC), |
63 | | - buttonLabel: 'ABC212 〜 318', |
64 | | - ariaLabel: 'Filter contests from ABC 212 to ABC 318', |
65 | | - }, |
66 | | - }; |
67 | | -
|
68 | | - type ContestTypeFilter = 'abcLatest20Rounds' | 'abc319Onwards' | 'abc212To318'; |
69 | | -
|
70 | | - let activeContestType = $state<ContestTypeFilter>('abcLatest20Rounds'); |
71 | | -
|
72 | | - // Select the task results based on the active contest type. |
73 | | - let taskResultsFilter: TaskResultsFilter = $derived( |
74 | | - contestFilterConfigs[activeContestType].filter(), |
75 | | - ); |
76 | | - let selectedTaskResults: TaskResults = $derived(taskResultsFilter.run()); |
| 34 | + let activeContestType = $state<ContestTableProviders>('abcLatest20Rounds'); |
77 | 35 |
|
78 | | - // Generate the task table based on the selected task results. |
79 | | - let taskTableGenerator: TaskTableGenerator = $derived( |
80 | | - contestFilterConfigs[activeContestType].table(selectedTaskResults), |
| 36 | + let provider: ContestTableProvider = $derived( |
| 37 | + contestTableProviders[activeContestType as ContestTableProviders], |
81 | 38 | ); |
82 | | - let taskTable: Record<string, Record<string, TaskResult>> = $derived(taskTableGenerator.run()); |
83 | | - let taskTableHeaderIds: Array<string> = $derived(taskTableGenerator.getHeaderIdsForTask()); |
84 | | - let contestIds: Array<string> = $derived(taskTableGenerator.getContestRoundIds()); |
85 | | -
|
86 | | - function getTaskTableTitle(taskTableGenerator: TaskTableGenerator): string { |
87 | | - return taskTableGenerator.getTitle() ?? ''; |
88 | | - } |
| 39 | + // Filter the task results based on the active contest type. |
| 40 | + let filteredTaskResults = $derived(provider.filter(taskResults)); |
| 41 | + // Generate the task table based on the filtered task results. |
| 42 | + let taskTable: Record<string, Record<string, TaskResult>> = $derived( |
| 43 | + provider.generateTable(filteredTaskResults), |
| 44 | + ); |
| 45 | + let taskTableHeaderIds: Array<string> = $derived( |
| 46 | + provider.getHeaderIdsForTask(filteredTaskResults), |
| 47 | + ); |
| 48 | + let contestIds: Array<string> = $derived(provider.getContestRoundIds(filteredTaskResults)); |
| 49 | + let title = $derived(provider.getMetadata().title); |
89 | 50 |
|
90 | | - function getContestRoundLabel(taskTableGenerator: TaskTableGenerator, contestId: string): string { |
91 | | - return taskTableGenerator.getContestRoundLabel(contestId); |
| 51 | + function getContestRoundLabel(provider: ContestTableProvider, contestId: string): string { |
| 52 | + return provider.getContestRoundLabel(contestId); |
92 | 53 | } |
93 | 54 |
|
94 | | - // FIXME: 他のコンポーネントと完全に重複しているので、コンポーネントとして切り出す。 |
95 | 55 | let updatingModal: UpdatingModal | null = null; |
96 | 56 |
|
97 | 57 | // WHY: () => updatingModal.openModal(taskResult) だけだと、updatingModalがnullの可能性があるため。 |
|
124 | 84 | <!-- See: --> |
125 | 85 | <!-- https://flowbite-svelte.com/docs/components/button-group --> |
126 | 86 | <ButtonGroup class="m-4 contents-center"> |
127 | | - {#each Object.entries(contestFilterConfigs) as [type, config]} |
| 87 | + {#each Object.entries(contestTableProviders) as [type, config]} |
128 | 88 | <Button |
129 | | - onclick={() => (activeContestType = type as ContestTypeFilter)} |
| 89 | + onclick={() => (activeContestType = type as ContestTableProviders)} |
130 | 90 | class={activeContestType === type ? 'active-button-class' : ''} |
131 | | - aria-label={config.ariaLabel} |
| 91 | + aria-label={config.getMetadata().ariaLabel} |
132 | 92 | > |
133 | | - {config.buttonLabel} |
| 93 | + {config.getMetadata().buttonLabel} |
134 | 94 | </Button> |
135 | 95 | {/each} |
136 | 96 | </ButtonGroup> |
137 | 97 |
|
138 | 98 | <Heading tag="h2" class="text-2xl pb-3 text-gray-900 dark:text-white"> |
139 | | - {getTaskTableTitle(taskTableGenerator)} |
| 99 | + {title} |
140 | 100 | </Heading> |
141 | 101 |
|
142 | 102 | <!-- TODO: ページネーションを実装 --> |
|
162 | 122 | {#each contestIds as contestId} |
163 | 123 | <TableBodyRow class="flex flex-wrap xl:table-row"> |
164 | 124 | <TableBodyCell class="w-full xl:w-16 truncate px-2 py-2 text-center border"> |
165 | | - {getContestRoundLabel(taskTableGenerator, contestId)} |
| 125 | + {getContestRoundLabel(provider, contestId)} |
166 | 126 | </TableBodyCell> |
167 | 127 |
|
168 | 128 | {#each taskTableHeaderIds as taskTableHeaderId} |
|
0 commit comments