Skip to content

Commit 52da577

Browse files
authored
Merge pull request #1875 from AtCoder-NoviSteps/#1874
🎨 Improve UI in contest table (#1874)
2 parents 11f90d1 + 3a34b50 commit 52da577

File tree

3 files changed

+73
-23
lines changed

3 files changed

+73
-23
lines changed

src/lib/components/SubmissionStatus/UpdatingDropdown.svelte

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// Component
2121
<button
2222
type="button"
23-
onclick={() => updatingDropdown.toggle()} // Open / close the dropdown.
23+
onclick={(event) => updatingDropdown.toggle(event)} // Open / close the dropdown.
2424
>
2525
2626
<UpdatingDropdown bind:this={updatingDropdown} {taskResult} {isLoggedIn} {onupdate} />
@@ -30,6 +30,7 @@
3030
import { enhance } from '$app/forms';
3131
3232
import { Dropdown, DropdownUl, DropdownLi, uiHelpers } from 'svelte-5-ui-lib';
33+
import Check from 'lucide-svelte/icons/check';
3334
3435
import type { TaskResult } from '$lib/types/task';
3536
@@ -55,15 +56,49 @@
5556
let dropdownStatus = $state(false);
5657
let closeDropdown = dropdown.close;
5758
59+
let dropdownX = $state(0);
60+
let dropdownY = $state(0);
61+
let isLowerHalfInScreen = $state(false);
62+
5863
$effect(() => {
5964
activeUrl = $page.url.pathname;
6065
dropdownStatus = dropdown.isOpen;
66+
67+
if (dropdownStatus) {
68+
document.documentElement.style.setProperty('--dropdown-x', `${dropdownX}px`);
69+
document.documentElement.style.setProperty('--dropdown-y', `${dropdownY}px`);
70+
}
6171
});
6272
63-
export function toggle(): void {
73+
export function toggle(event?: MouseEvent): void {
74+
if (event) {
75+
getDropdownPosition(event);
76+
}
77+
6478
dropdown.toggle();
6579
}
6680
81+
function getDropdownPosition(event: MouseEvent): void {
82+
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
83+
84+
dropdownX = rect.right;
85+
dropdownY = rect.bottom;
86+
87+
isLowerHalfInScreen = rect.top > window.innerHeight / 2;
88+
}
89+
90+
function getDropdownClasses(isLower: boolean): string {
91+
let classes =
92+
'absolute w-32 z-[999] shadow-lg pointer-events-auto left-[var(--dropdown-x)] transform -translate-x-full ';
93+
94+
if (isLower) {
95+
classes += 'bottom-[calc(100vh-var(--dropdown-y))] mb-5';
96+
} else {
97+
classes += 'top-[var(--dropdown-y)] mt-1';
98+
}
99+
return classes;
100+
}
101+
67102
let selectedSubmissionStatus = $state<SubmissionStatus>();
68103
let showForm = $state(false);
69104
@@ -175,19 +210,17 @@
175210
});
176211
</script>
177212

178-
<div class="relative">
213+
<div class="fixed inset-0 pointer-events-none z-50 w-full h-full">
179214
<Dropdown
180215
{activeUrl}
181216
{dropdownStatus}
182217
{closeDropdown}
183-
class="absolute w-32 z-20 left-auto right-0 mt-8"
218+
class={getDropdownClasses(isLowerHalfInScreen)}
184219
>
185-
<DropdownUl>
220+
<DropdownUl class="border rounded-lg shadow">
186221
{#if isLoggedIn}
187222
{#each submissionStatusOptions as submissionStatus}
188-
<DropdownLi href="javascript:void(0)" onclick={() => handleClick(submissionStatus)}>
189-
{submissionStatus.labelName}
190-
</DropdownLi>
223+
{@render dropdownListForSubmissionStatus(taskResult, submissionStatus)}
191224
{/each}
192225
{:else}
193226
<DropdownLi href={SIGNUP_PAGE}>アカウント作成</DropdownLi>
@@ -201,6 +234,21 @@
201234
{/if}
202235
</div>
203236

237+
{#snippet dropdownListForSubmissionStatus(
238+
taskResult: TaskResult,
239+
submissionStatus: SubmissionStatus,
240+
)}
241+
<DropdownLi href="javascript:void(0)" onclick={() => handleClick(submissionStatus)}>
242+
<div class="flex items-center justify-between">
243+
{submissionStatus.labelName}
244+
245+
{#if taskResult.status_name === submissionStatus.innerName}
246+
<Check class="w-4 h-4 text-primary-600 dark:text-gray-300" strokeWidth={3} />
247+
{/if}
248+
</div>
249+
</DropdownLi>
250+
{/snippet}
251+
204252
{#snippet submissionStatusForm(selectedTaskResult: TaskResult, submissionStatus: SubmissionStatus)}
205253
<form
206254
id="submissionStatusForm"

src/lib/components/TaskTables/TaskTable.svelte

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
}
5555
5656
function getBodyCellClasses(contestId: string, taskIndex: string): string {
57-
const baseClasses = 'w-1/2 xs:w-1/3 sm:w-1/4 md:w-1/5 lg:w-1/6 px-1 py-1 border';
57+
const baseClasses = 'w-1/2 xs:w-1/3 sm:w-1/4 md:w-1/5 lg:w-1/6 px-1 py-1';
5858
const backgroundColor = getBackgroundColor(taskTable[contestId][taskIndex]);
5959
6060
return `${baseClasses} ${backgroundColor}`;
@@ -129,34 +129,34 @@
129129
</Heading>
130130

131131
<!-- TODO: ページネーションを実装 -->
132-
<!-- TODO: ヘッダーを固定できるようにする。-->
133-
<!-- HACK: Flowbite と tailwindcss の相性が悪いのかもしれない。tailwindcss のクラス指定、raw HTML & CSS を試したが、いずれも実現できず。 -->
134132
<!-- See: -->
135133
<!-- https://github.com/kenkoooo/AtCoderProblems/blob/master/atcoder-problems-frontend/src/pages/TablePage/AtCoderRegularTable.tsx -->
136134
<!-- https://github.com/birdou/atcoder-blogs/blob/main/app/atcoder-blogs-frontend/src/pages/BlogTablePage/BlogTablePage.tsx -->
137135
<!-- https://tailwindcss.com/docs/position#sticky-positioning-elements -->
138-
<div class="container w-full overflow-hidden rounded-md border border-gray-100 shadow-sm">
139-
<div class="w-full overflow-auto">
136+
<div class="container w-full rounded-md border shadow-sm">
137+
<div class="w-full sticky top-0 z-20 border-b">
140138
<Table id="task-table" class="text-md table-fixed w-full" aria-label="Task table">
141139
<TableHead class="text-sm bg-gray-100">
142-
<TableHeadCell class="w-full xl:w-16 px-2 text-center border" scope="col">
143-
Round
144-
</TableHeadCell>
140+
<TableHeadCell class="w-full xl:w-16 px-2 text-center" scope="col">Round</TableHeadCell>
145141

146142
{#if taskTableHeaderIds.length}
147143
{#each taskTableHeaderIds as taskTableHeaderId}
148-
<TableHeadCell class="text-center border" scope="col">
144+
<TableHeadCell class="text-center" scope="col">
149145
{taskTableHeaderId}
150146
</TableHeadCell>
151147
{/each}
152148
{/if}
153149
</TableHead>
150+
</Table>
151+
</div>
154152

153+
<div class="w-full overflow-auto max-h-[calc(80vh-56px)]">
154+
<Table id="task-table" class="text-md table-fixed w-full" aria-label="Task table">
155155
<TableBody class="divide-y">
156156
{#if contestIds.length && taskTableHeaderIds.length}
157157
{#each contestIds as contestId}
158158
<TableBodyRow class="flex flex-wrap xl:table-row">
159-
<TableBodyCell class="w-full xl:w-16 truncate px-2 py-2 text-center border">
159+
<TableBodyCell class="w-full xl:w-16 truncate px-2 py-2 text-center">
160160
{getContestRoundLabel(provider, contestId)}
161161
</TableBodyCell>
162162

src/lib/components/TaskTables/TaskTableBodyCell.svelte

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import EllipsisVertical from 'lucide-svelte/icons/ellipsis-vertical';
2+
import ChevronDown from 'lucide-svelte/icons/chevron-down';
33
44
import type { TaskResult } from '$lib/types/task';
55
@@ -20,10 +20,12 @@
2020
let updatingDropdown: UpdatingDropdown;
2121
</script>
2222

23-
<div class="flex items-center w-full space-x-1 text-left text-sm sm:text-md">
23+
<div
24+
class="flex items-center w-full pl-0 lg:pl-1 space-x-1 lg:space-x-2 text-left text-sm sm:text-md"
25+
>
2426
{@render taskGradeLabel(taskResult)}
2527

26-
<div class="flex justify-between w-full min-w-0">
28+
<div class="flex items-center justify-between w-full min-w-0">
2729
{@render taskTitleAndExternalLink(taskResult)}
2830
{@render submissionUpdaterAndLinksOfTaskDetailPage(taskResult)}
2931
</div>
@@ -59,10 +61,10 @@
5961
<button
6062
type="button"
6163
class="flex-shrink-0 w-6 ml-auto"
62-
onclick={() => updatingDropdown.toggle()}
64+
onclick={(event) => updatingDropdown.toggle(event)}
6365
aria-label="Update submission for {selectedTaskResult.title}"
6466
>
65-
<EllipsisVertical class="w-4 h-4 mx-auto" />
67+
<ChevronDown class="w-4 h-4 mx-auto" />
6668
</button>
6769

6870
<UpdatingDropdown bind:this={updatingDropdown} {taskResult} {isLoggedIn} {onupdate} />

0 commit comments

Comments
 (0)