Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🎨 Improve grade colors (#1631) #1877

Merged
merged 5 commits into from
Apr 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions src/lib/components/GradeLabel.svelte
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
<script lang="ts">
import { TaskGrade } from '$lib/types/task';
import { getTaskGradeColor, getTaskGradeLabel, toWhiteTextIfNeeds } from '$lib/utils/task';
import {
getTaskGradeColor,
getTaskGradeLabel,
toChangeTextColorIfNeeds,
toChangeBorderColorIfNeeds,
} from '$lib/utils/task';

interface Props {
taskGrade: TaskGrade | string;
defaultPadding?: number;
defaultWidth?: number;
reducedWidth?: number;
defaultTextSize?: string;
}

let { taskGrade, defaultPadding = 1, defaultWidth = 10, reducedWidth = 8 }: Props = $props();
let {
taskGrade,
defaultPadding = 1,
defaultWidth = 10,
reducedWidth = 8,
defaultTextSize = 'md',
}: Props = $props();

let grade = $derived(getTaskGradeLabel(taskGrade));
let gradeColor = $derived(getTaskGradeColor(taskGrade));
</script>

<div class="rounded-lg border-2 border-white">
<div class="rounded-lg border-2 {toChangeBorderColorIfNeeds(grade)}">
<div
class="p-{defaultPadding} w-{reducedWidth} xs:w-{defaultWidth} text-sm xs:text-md text-center rounded-md {toWhiteTextIfNeeds(
class="p-{defaultPadding} w-{reducedWidth} xs:w-{defaultWidth} text-sm xs:text-{defaultTextSize} text-center rounded-md {toChangeTextColorIfNeeds(
grade,
)} {gradeColor}"
>
Expand Down
5 changes: 1 addition & 4 deletions src/lib/components/TaskGradeList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

import TaskList from '$lib/components/TaskList.svelte';

import { getTaskGradeColor, getTaskGradeLabel } from '$lib/utils/task';

import type { TaskResults, TaskResult } from '$lib/types/task';
import { TaskGrade, taskGradeValues } from '$lib/types/task';

Expand Down Expand Up @@ -53,8 +51,7 @@
<!-- HACK: Svelteでcontinueに相当する構文は確認できず(2024年1月時点)。 -->
{#if countTasks(taskGrade) && isShowTaskList(isAdmin, taskGrade)}
<TaskList
grade={getTaskGradeLabel(taskGrade)}
gradeColor={getTaskGradeColor(taskGrade)}
grade={taskGrade}
taskResults={taskResultsForEachGrade.get(taskGrade)}
{isAdmin}
{isLoggedIn}
Expand Down
26 changes: 11 additions & 15 deletions src/lib/components/TaskList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
TableHeadCell,
} from 'svelte-5-ui-lib';

import { type TaskResult, type TaskResults, TaskGrade } from '$lib/types/task';
import { type TaskResult, type TaskResults } from '$lib/types/task';

import GradeLabel from '$lib/components/GradeLabel.svelte';
import ThermometerProgressBar from '$lib/components/ThermometerProgressBar.svelte';
import UpdatingModal from '$lib/components/SubmissionStatus/UpdatingModal.svelte';
import SubmissionStatusImage from '$lib/components/SubmissionStatus/SubmissionStatusImage.svelte';
Expand All @@ -21,17 +22,16 @@
import { getBackgroundColorFrom } from '$lib/services/submission_status';

import { addContestNameToTaskIndex } from '$lib/utils/contest';
import { toWhiteTextIfNeeds, getTaskUrl, removeTaskIndexFromTitle } from '$lib/utils/task';
import { getTaskUrl, removeTaskIndexFromTitle } from '$lib/utils/task';

interface Props {
grade: string;
gradeColor: string;
taskResults: TaskResults;
isAdmin: boolean;
isLoggedIn: boolean;
}

let { grade, gradeColor, taskResults, isAdmin, isLoggedIn }: Props = $props();
let { grade, taskResults, isAdmin, isLoggedIn }: Props = $props();

// TODO: 他のコンポーネントでも利用できるようにする。
let updatingModal: UpdatingModal | null = null;
Expand All @@ -49,17 +49,13 @@
<AccordionItem>
{#snippet header()}
<span class="flex justify-around w-full place-items-center">
<div
class="text-sm xs:text-xl w-9 xs:w-12 p-0.5 text-center rounded-lg {toWhiteTextIfNeeds(
grade,
)} {gradeColor}"
>
{#if grade !== TaskGrade.PENDING}
{grade}
{:else}
??
{/if}
</div>
<GradeLabel
taskGrade={grade}
defaultPadding={0.25}
defaultWidth={12}
reducedWidth={9}
defaultTextSize="xl"
/>

<ThermometerProgressBar {taskResults} />
<AcceptedCounter {taskResults} />
Expand Down
16 changes: 12 additions & 4 deletions src/lib/utils/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,20 +263,28 @@ export const getTaskGradeLabel = (taskGrade: TaskGrade | string) => {

// See:
// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
export const toWhiteTextIfNeeds = (grade: string) => {
export const toChangeTextColorIfNeeds = (grade: string): string => {
const gradeToWhiteText = [
`${getTaskGradeLabel(TaskGrade.Q3)}`,
`${getTaskGradeLabel(TaskGrade.Q2)}`,
`${getTaskGradeLabel(TaskGrade.D1)}`,
`${getTaskGradeLabel(TaskGrade.D2)}`,
`${getTaskGradeLabel(TaskGrade.D3)}`,
`${getTaskGradeLabel(TaskGrade.D4)}`,
`${getTaskGradeLabel(TaskGrade.D5)}`,
`${getTaskGradeLabel(TaskGrade.D6)}`,
];

if (gradeToWhiteText.includes(grade)) {
return 'text-white';
} else if (getTaskGradeLabel(grade) === TaskGrade.D6) {
return 'text-atcoder-bronze';
} else {
return 'text-black';
}
};

export const toChangeBorderColorIfNeeds = (grade: string): string => {
if (getTaskGradeLabel(grade) === TaskGrade.D6) {
return 'border-atcoder-bronze';
}

return 'border-white';
};
105 changes: 105 additions & 0 deletions src/test/lib/utils/task_grade.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { expect, test } from 'vitest';

import { runTests } from '../common/test_helpers';
import {
getGradeOrder,
taskGradeOrderInfinity,
calcGradeMode,
getTaskGradeLabel,
toChangeTextColorIfNeeds,
toChangeBorderColorIfNeeds,
} from '$lib/utils/task';

import { TaskGrade, type TaskGrades } from '$lib/types/task';
Expand All @@ -30,6 +33,13 @@ type TestCaseForTaskGradeLabel = {

type TestCasesForTaskGradeLabel = TestCaseForTaskGradeLabel[];

type TestCaseForTaskGrade = {
taskGrade: TaskGrade;
expected: string;
};

type TestCasesForTaskGrade = TestCaseForTaskGrade[];

// See: src/lib/utils/task.ts
//
// Note: We split the test file by task and task grade because there are more test cases.
Expand Down Expand Up @@ -463,4 +473,99 @@ describe('Task grade', () => {
test.each(testCases)(`${testName}(taskGrade: $taskGrade)`, testFunction);
}
});

describe('to change text color if needs', () => {
describe('when task grades from 11Q to 1Q are given ', () => {
const testCases: TestCasesForTaskGrade = [
{ taskGrade: TaskGrade.Q11, expected: 'text-black' },
{ taskGrade: TaskGrade.Q10, expected: 'text-black' },
{ taskGrade: TaskGrade.Q6, expected: 'text-black' },
{ taskGrade: TaskGrade.Q5, expected: 'text-black' },
{ taskGrade: TaskGrade.Q2, expected: 'text-black' },
{ taskGrade: TaskGrade.Q1, expected: 'text-black' },
];

runTests(
'toChangeTextColorIfNeeds',
testCases,
({ taskGrade, expected }: TestCaseForTaskGrade) => {
expect(toChangeTextColorIfNeeds(getTaskGradeLabel(taskGrade))).toBe(expected);
},
);
});

describe('when task grades from 1D to 5D are given', () => {
const testCases: TestCasesForTaskGrade = [
{ taskGrade: TaskGrade.D1, expected: 'text-white' },
{ taskGrade: TaskGrade.D2, expected: 'text-white' },
{ taskGrade: TaskGrade.D3, expected: 'text-white' },
{ taskGrade: TaskGrade.D4, expected: 'text-white' },
{ taskGrade: TaskGrade.D5, expected: 'text-white' },
];

runTests(
'toChangeTextColorIfNeeds',
testCases,
({ taskGrade, expected }: TestCaseForTaskGrade) => {
expect(toChangeTextColorIfNeeds(getTaskGradeLabel(taskGrade))).toBe(expected);
},
);
});

describe('when task grades 6D is given', () => {
const testCases: TestCasesForTaskGrade = [
{ taskGrade: TaskGrade.D6, expected: 'text-atcoder-bronze' },
];

runTests(
'toChangeTextColorIfNeeds',
testCases,
({ taskGrade, expected }: TestCaseForTaskGrade) => {
expect(toChangeTextColorIfNeeds(getTaskGradeLabel(taskGrade))).toBe(expected);
},
);
});
});

describe('to change border color if needs', () => {
describe('when task grades from 11Q to 5D are given', () => {
const testCasesForQGrades = [
{ taskGrade: TaskGrade.Q11, expected: 'border-white' },
{ taskGrade: TaskGrade.Q10, expected: 'border-white' },
{ taskGrade: TaskGrade.Q7, expected: 'border-white' },
{ taskGrade: TaskGrade.Q6, expected: 'border-white' },
{ taskGrade: TaskGrade.Q2, expected: 'border-white' },
{ taskGrade: TaskGrade.Q1, expected: 'border-white' },
];
const testCasesForDGrades = [
{ taskGrade: TaskGrade.D1, expected: 'border-white' },
{ taskGrade: TaskGrade.D2, expected: 'border-white' },
{ taskGrade: TaskGrade.D4, expected: 'border-white' },
{ taskGrade: TaskGrade.D5, expected: 'border-white' },
];
const testCases: TestCasesForTaskGrade = [...testCasesForQGrades, ...testCasesForDGrades];

runTests(
'toChangeBorderColorIfNeeds',
testCases,
({ taskGrade, expected }: TestCaseForTaskGrade) => {
expect(toChangeBorderColorIfNeeds(getTaskGradeLabel(taskGrade))).toBe(expected);
},
);
});

describe('when task grade 6D is given ', () => {
const testCases: TestCasesForTaskGrade = [
{ taskGrade: TaskGrade.D6, expected: 'border-atcoder-bronze' },
];

runTests(
'toChangeBorderColorIfNeeds',
testCases,
({ taskGrade, expected }: TestCaseForTaskGrade) => {
expect(toChangeBorderColorIfNeeds(getTaskGradeLabel(taskGrade))).toBe(expected);
},
);
});
});
});
36 changes: 18 additions & 18 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,23 @@ const config = {
900: '#159957',
},
atcoder: {
Q11: '#e4e3e3',
Q10: '#e4dfdc',
Q9: '#dde3dc',
Q8: '#dde7e7',
Q7: '#dddceb',
Q6: '#e8e7dc',
Q5: '#ece3dc',
Q4: '#ecdcdc',
Q3: '#741b47',
Q2: '#7f6000',
Q1: '#01fb02',
D1: '#72C6ef',
D2: '#002eff',
D3: '#ffff02',
D4: '#ff9900',
D5: '#ff1000',
D6: '#cc0A00',
Q11: '#e4d3da',
Q10: '#e3cad8',
Q9: '#e4cde0',
Q8: '#e4cbe8',
Q7: '#d0afd6',
Q6: '#d9afe5',
Q5: '#f7cc52',
Q4: '#f1dd72',
Q3: '#c8e389',
Q2: '#97d093',
Q1: '#73d091',
D1: '#1c85b6',
D2: '#7f36be',
D3: '#e68e2e',
D4: '#e36223',
D5: '#e60d00',
D6: '#432414',
gray: '',
brown: '',
green: '',
Expand All @@ -58,7 +58,7 @@ const config = {
yellow: '',
orange: '',
red: '',
bronze: '',
bronze: '#e38a66',
silver: '',
gold: '',
ns: '#ffffff',
Expand Down
Loading