mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-04-05 17:33:31 -04:00
Fixed instructions, added color border based on closeness between guess
and target
This commit is contained in:
@@ -1,309 +1,355 @@
|
||||
<script lang="ts">
|
||||
import { bibleBooks, type BibleBook, type BibleSection, type Testament } from "$lib/types/bible";
|
||||
import { SvelteSet } from "svelte/reactivity";
|
||||
import {
|
||||
bibleBooks,
|
||||
type BibleBook,
|
||||
type BibleSection,
|
||||
type Testament,
|
||||
} from "$lib/types/bible";
|
||||
import { SvelteSet } from "svelte/reactivity";
|
||||
|
||||
let {
|
||||
searchQuery = $bindable(""),
|
||||
guessedIds,
|
||||
submitGuess,
|
||||
guessCount = 0,
|
||||
}: {
|
||||
searchQuery: string;
|
||||
guessedIds: SvelteSet<string>;
|
||||
submitGuess: (id: string) => void;
|
||||
guessCount: number;
|
||||
} = $props();
|
||||
let {
|
||||
searchQuery = $bindable(""),
|
||||
guessedIds,
|
||||
submitGuess,
|
||||
guessCount = 0,
|
||||
}: {
|
||||
searchQuery: string;
|
||||
guessedIds: SvelteSet<string>;
|
||||
submitGuess: (id: string) => void;
|
||||
guessCount: number;
|
||||
} = $props();
|
||||
|
||||
type DisplayMode = "simple" | "testament" | "sections";
|
||||
type DisplayMode = "simple" | "testament" | "sections";
|
||||
|
||||
const displayMode = $derived<DisplayMode>(
|
||||
guessCount >= 9 ? "sections" : guessCount >= 3 ? "testament" : "simple"
|
||||
);
|
||||
const displayMode = $derived<DisplayMode>(
|
||||
guessCount >= 9 ? "sections" : guessCount >= 3 ? "testament" : "simple",
|
||||
);
|
||||
|
||||
const filteredBooks = $derived(
|
||||
bibleBooks.filter((book) =>
|
||||
book.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
);
|
||||
const filteredBooks = $derived(
|
||||
bibleBooks.filter((book) =>
|
||||
book.name.toLowerCase().includes(searchQuery.toLowerCase()),
|
||||
),
|
||||
);
|
||||
|
||||
type SimpleGroup = { books: BibleBook[] };
|
||||
type SimpleGroup = { books: BibleBook[] };
|
||||
|
||||
type TestamentGroup = {
|
||||
testament: Testament;
|
||||
label: string;
|
||||
books: BibleBook[];
|
||||
};
|
||||
type TestamentGroup = {
|
||||
testament: Testament;
|
||||
label: string;
|
||||
books: BibleBook[];
|
||||
};
|
||||
|
||||
type SectionGroup = {
|
||||
testament: Testament;
|
||||
testamentLabel: string;
|
||||
showTestamentHeader: boolean;
|
||||
section: BibleSection;
|
||||
books: BibleBook[];
|
||||
};
|
||||
type SectionGroup = {
|
||||
testament: Testament;
|
||||
testamentLabel: string;
|
||||
showTestamentHeader: boolean;
|
||||
section: BibleSection;
|
||||
books: BibleBook[];
|
||||
};
|
||||
|
||||
const simpleGroup = $derived.by<SimpleGroup>(() => {
|
||||
const sorted = [...filteredBooks].sort((a, b) =>
|
||||
a.name.localeCompare(b.name)
|
||||
);
|
||||
return { books: sorted };
|
||||
});
|
||||
const simpleGroup = $derived.by<SimpleGroup>(() => {
|
||||
const sorted = [...filteredBooks].sort((a, b) =>
|
||||
a.name.localeCompare(b.name),
|
||||
);
|
||||
return { books: sorted };
|
||||
});
|
||||
|
||||
const testamentGroups = $derived.by<TestamentGroup[]>(() => {
|
||||
const old = filteredBooks
|
||||
.filter((b) => b.testament === "old")
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
const newT = filteredBooks
|
||||
.filter((b) => b.testament === "new")
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
const groups: TestamentGroup[] = [];
|
||||
if (old.length > 0) {
|
||||
groups.push({ testament: "old", label: "Old Testament", books: old });
|
||||
}
|
||||
if (newT.length > 0) {
|
||||
groups.push({ testament: "new", label: "New Testament", books: newT });
|
||||
}
|
||||
return groups;
|
||||
});
|
||||
const testamentGroups = $derived.by<TestamentGroup[]>(() => {
|
||||
const old = filteredBooks
|
||||
.filter((b) => b.testament === "old")
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
const newT = filteredBooks
|
||||
.filter((b) => b.testament === "new")
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
const groups: TestamentGroup[] = [];
|
||||
if (old.length > 0) {
|
||||
groups.push({
|
||||
testament: "old",
|
||||
label: "Old Testament",
|
||||
books: old,
|
||||
});
|
||||
}
|
||||
if (newT.length > 0) {
|
||||
groups.push({
|
||||
testament: "new",
|
||||
label: "New Testament",
|
||||
books: newT,
|
||||
});
|
||||
}
|
||||
return groups;
|
||||
});
|
||||
|
||||
const sectionGroups = $derived.by<SectionGroup[]>(() => {
|
||||
// Build an ordered list of (testament, section) pairs by iterating bibleBooks once
|
||||
const seenKeys: Record<string, true> = {};
|
||||
const orderedPairs: { testament: Testament; section: BibleSection }[] = [];
|
||||
const sectionGroups = $derived.by<SectionGroup[]>(() => {
|
||||
// Build an ordered list of (testament, section) pairs by iterating bibleBooks once
|
||||
const seenKeys: Record<string, true> = {};
|
||||
const orderedPairs: { testament: Testament; section: BibleSection }[] =
|
||||
[];
|
||||
|
||||
for (const book of bibleBooks) {
|
||||
const key = `${book.testament}:${book.section}`;
|
||||
if (!seenKeys[key]) {
|
||||
seenKeys[key] = true;
|
||||
orderedPairs.push({ testament: book.testament, section: book.section });
|
||||
}
|
||||
}
|
||||
for (const book of bibleBooks) {
|
||||
const key = `${book.testament}:${book.section}`;
|
||||
if (!seenKeys[key]) {
|
||||
seenKeys[key] = true;
|
||||
orderedPairs.push({
|
||||
testament: book.testament,
|
||||
section: book.section,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const groups: SectionGroup[] = [];
|
||||
let lastTestament: Testament | null = null;
|
||||
const groups: SectionGroup[] = [];
|
||||
let lastTestament: Testament | null = null;
|
||||
|
||||
for (const pair of orderedPairs) {
|
||||
const books = filteredBooks.filter(
|
||||
(b) => b.testament === pair.testament && b.section === pair.section
|
||||
);
|
||||
if (books.length === 0) continue;
|
||||
for (const pair of orderedPairs) {
|
||||
const books = filteredBooks.filter(
|
||||
(b) =>
|
||||
b.testament === pair.testament &&
|
||||
b.section === pair.section,
|
||||
);
|
||||
if (books.length === 0) continue;
|
||||
|
||||
const showTestamentHeader = pair.testament !== lastTestament;
|
||||
lastTestament = pair.testament;
|
||||
const showTestamentHeader = pair.testament !== lastTestament;
|
||||
lastTestament = pair.testament;
|
||||
|
||||
groups.push({
|
||||
testament: pair.testament,
|
||||
testamentLabel:
|
||||
pair.testament === "old" ? "Old Testament" : "New Testament",
|
||||
showTestamentHeader,
|
||||
section: pair.section,
|
||||
books,
|
||||
});
|
||||
}
|
||||
groups.push({
|
||||
testament: pair.testament,
|
||||
testamentLabel:
|
||||
pair.testament === "old"
|
||||
? "Old Testament"
|
||||
: "New Testament",
|
||||
showTestamentHeader,
|
||||
section: pair.section,
|
||||
books,
|
||||
});
|
||||
}
|
||||
|
||||
return groups;
|
||||
});
|
||||
return groups;
|
||||
});
|
||||
|
||||
// First book in display order for Enter key submission
|
||||
const firstBookId = $derived.by<string | null>(() => {
|
||||
if (filteredBooks.length === 0) return null;
|
||||
if (displayMode === "simple") {
|
||||
return simpleGroup.books[0]?.id ?? null;
|
||||
}
|
||||
if (displayMode === "testament") {
|
||||
return testamentGroups[0]?.books[0]?.id ?? null;
|
||||
}
|
||||
return sectionGroups[0]?.books[0]?.id ?? null;
|
||||
});
|
||||
// First book in display order for Enter key submission
|
||||
const firstBookId = $derived.by<string | null>(() => {
|
||||
if (filteredBooks.length === 0) return null;
|
||||
if (displayMode === "simple") {
|
||||
return simpleGroup.books[0]?.id ?? null;
|
||||
}
|
||||
if (displayMode === "testament") {
|
||||
return testamentGroups[0]?.books[0]?.id ?? null;
|
||||
}
|
||||
return sectionGroups[0]?.books[0]?.id ?? null;
|
||||
});
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.key === "Enter" && firstBookId) {
|
||||
submitGuess(firstBookId);
|
||||
}
|
||||
}
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.key === "Enter" && firstBookId) {
|
||||
submitGuess(firstBookId);
|
||||
}
|
||||
}
|
||||
|
||||
const showBanner = $derived(guessCount >= 3);
|
||||
const bannerIsIndigo = $derived(guessCount >= 9);
|
||||
// const showBanner = $derived(guessCount >= 3);
|
||||
const showBanner = false;
|
||||
const bannerIsIndigo = $derived(guessCount >= 9);
|
||||
</script>
|
||||
|
||||
{#if showBanner}
|
||||
<p
|
||||
class="mb-3 text-xs font-medium text-gray-500 dark:text-gray-400"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
{#if bannerIsIndigo}
|
||||
Testament & section groups now visible
|
||||
{:else}
|
||||
Old & New Testament groups now visible
|
||||
{/if}
|
||||
</p>
|
||||
<p
|
||||
class="mb-3 text-xs font-medium text-gray-500 dark:text-gray-400"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
{#if bannerIsIndigo}
|
||||
Testament & section groups now visible
|
||||
{:else}
|
||||
Old & New Testament groups now visible
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<div class="relative">
|
||||
<div class="relative">
|
||||
<svg
|
||||
class="absolute left-4 sm:left-6 top-1/2 -translate-y-1/2 w-5 h-5 sm:w-6 sm:h-6 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
<input
|
||||
bind:value={searchQuery}
|
||||
placeholder="Type to guess a book (e.g. 'Genesis', 'John')..."
|
||||
class="w-full pl-12 sm:pl-16 p-4 sm:p-6 border-2 border-gray-500 dark:border-gray-600 rounded-2xl text-base sm:text-lg md:text-xl focus:outline-none focus:border-blue-600 dark:focus:border-blue-400 focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-900/50 transition-all bg-white dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400"
|
||||
onkeydown={handleKeydown}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{#if searchQuery}
|
||||
<button
|
||||
class="absolute right-4 sm:right-6 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors"
|
||||
onclick={() => (searchQuery = "")}
|
||||
aria-label="Clear search"
|
||||
>
|
||||
<svg
|
||||
class="w-5 h-5 sm:w-6 sm:h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="relative">
|
||||
<svg
|
||||
class="absolute left-4 sm:left-6 top-1/2 -translate-y-1/2 w-5 h-5 sm:w-6 sm:h-6 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
<input
|
||||
bind:value={searchQuery}
|
||||
placeholder="Type to guess a book (e.g. 'Genesis', 'John')..."
|
||||
class="w-full pl-12 sm:pl-16 p-4 sm:p-6 border-2 border-gray-500 dark:border-gray-600 rounded-2xl text-base sm:text-lg md:text-xl focus:outline-none focus:border-blue-600 dark:focus:border-blue-400 focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-900/50 transition-all bg-white dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400"
|
||||
onkeydown={handleKeydown}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{#if searchQuery}
|
||||
<button
|
||||
class="absolute right-4 sm:right-6 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors"
|
||||
onclick={() => (searchQuery = "")}
|
||||
aria-label="Clear search"
|
||||
>
|
||||
<svg
|
||||
class="w-5 h-5 sm:w-6 sm:h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if searchQuery && filteredBooks.length > 0}
|
||||
<ul
|
||||
class="mt-4 max-h-60 sm:max-h-80 overflow-y-auto bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-2xl shadow-xl"
|
||||
role="listbox"
|
||||
>
|
||||
{#if displayMode === "simple"}
|
||||
{#each simpleGroup.books as book (book.id)}
|
||||
<li role="option" aria-selected={guessedIds.has(book.id)}>
|
||||
<button
|
||||
class="w-full px-5 py-4 text-left border-b border-gray-100 last:border-b-0 flex items-center transition-all
|
||||
{#if searchQuery && filteredBooks.length > 0}
|
||||
<ul
|
||||
class="mt-4 max-h-60 sm:max-h-80 overflow-y-auto bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-2xl shadow-xl"
|
||||
role="listbox"
|
||||
>
|
||||
{#if displayMode === "simple"}
|
||||
{#each simpleGroup.books as book (book.id)}
|
||||
<li role="option" aria-selected={guessedIds.has(book.id)}>
|
||||
<button
|
||||
class="w-full px-5 py-4 text-left border-b border-gray-100 last:border-b-0 flex items-center transition-all
|
||||
{guessedIds.has(book.id)
|
||||
? 'opacity-50 cursor-not-allowed pointer-events-none'
|
||||
: 'hover:bg-blue-50 hover:text-blue-700'}"
|
||||
onclick={() => submitGuess(book.id)}
|
||||
tabindex={guessedIds.has(book.id) ? -1 : 0}
|
||||
>
|
||||
<span
|
||||
class="font-semibold dark:text-gray-100 {guessedIds.has(book.id)
|
||||
? 'line-through text-gray-400 dark:text-gray-500'
|
||||
: ''}"
|
||||
>
|
||||
{book.name}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
{:else if displayMode === "testament"}
|
||||
{#each testamentGroups as group (group.testament)}
|
||||
<li role="presentation">
|
||||
<div
|
||||
class="px-5 py-2 flex items-center gap-3 bg-gray-50 dark:bg-gray-700/50 border-b border-gray-100 dark:border-gray-700"
|
||||
>
|
||||
<span
|
||||
class="text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-400"
|
||||
>
|
||||
{group.label}
|
||||
</span>
|
||||
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-600"></div>
|
||||
</div>
|
||||
<ul>
|
||||
{#each group.books as book (book.id)}
|
||||
<li role="option" aria-selected={guessedIds.has(book.id)}>
|
||||
<button
|
||||
class="w-full px-5 py-4 text-left border-b border-gray-100 dark:border-gray-700 last:border-b-0 flex items-center transition-all dark:text-gray-200
|
||||
? 'opacity-50 cursor-not-allowed pointer-events-none'
|
||||
: 'hover:bg-blue-50 hover:text-blue-700'}"
|
||||
onclick={() => submitGuess(book.id)}
|
||||
tabindex={guessedIds.has(book.id) ? -1 : 0}
|
||||
>
|
||||
<span
|
||||
class="font-semibold dark:text-gray-100 {guessedIds.has(
|
||||
book.id,
|
||||
)
|
||||
? 'line-through text-gray-400 dark:text-gray-500'
|
||||
: ''}"
|
||||
>
|
||||
{book.name}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
{:else if displayMode === "testament"}
|
||||
{#each testamentGroups as group (group.testament)}
|
||||
<li role="presentation">
|
||||
<div
|
||||
class="px-5 py-2 flex items-center gap-3 bg-gray-50 dark:bg-gray-700/50 border-b border-gray-100 dark:border-gray-700"
|
||||
>
|
||||
<span
|
||||
class="text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-400"
|
||||
>
|
||||
{group.label}
|
||||
</span>
|
||||
<div
|
||||
class="flex-1 h-px bg-gray-200 dark:bg-gray-600"
|
||||
></div>
|
||||
</div>
|
||||
<ul>
|
||||
{#each group.books as book (book.id)}
|
||||
<li
|
||||
role="option"
|
||||
aria-selected={guessedIds.has(book.id)}
|
||||
>
|
||||
<button
|
||||
class="w-full px-5 py-4 text-left border-b border-gray-100 dark:border-gray-700 last:border-b-0 flex items-center transition-all dark:text-gray-200
|
||||
{guessedIds.has(book.id)
|
||||
? 'opacity-50 cursor-not-allowed pointer-events-none'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300'}"
|
||||
onclick={() => submitGuess(book.id)}
|
||||
tabindex={guessedIds.has(book.id) ? -1 : 0}
|
||||
>
|
||||
<span
|
||||
class="font-semibold {guessedIds.has(book.id)
|
||||
? 'line-through text-gray-400 dark:text-gray-500'
|
||||
: ''}"
|
||||
>
|
||||
{book.name}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</li>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each sectionGroups as group (`${group.testament}:${group.section}`)}
|
||||
<li role="presentation">
|
||||
{#if group.showTestamentHeader}
|
||||
<div
|
||||
class="px-5 pt-3 pb-1 flex items-center gap-3 bg-gray-50 dark:bg-gray-700/50 border-b border-gray-100 dark:border-gray-700"
|
||||
>
|
||||
<span
|
||||
class="text-xs font-bold uppercase tracking-wider text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{group.testamentLabel}
|
||||
</span>
|
||||
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-600"></div>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="px-7 py-1.5 flex items-center gap-3 bg-gray-50/50 dark:bg-gray-700/30 border-b border-gray-100 dark:border-gray-700"
|
||||
>
|
||||
<span
|
||||
class="text-[11px] font-medium uppercase tracking-wider text-gray-400 dark:text-gray-500"
|
||||
>
|
||||
{group.section}
|
||||
</span>
|
||||
<div class="flex-1 h-px bg-gray-100 dark:bg-gray-600"></div>
|
||||
</div>
|
||||
<ul>
|
||||
{#each group.books as book (book.id)}
|
||||
<li role="option" aria-selected={guessedIds.has(book.id)}>
|
||||
<button
|
||||
class="w-full px-5 py-4 text-left border-b border-gray-100 dark:border-gray-700 last:border-b-0 flex items-center transition-all dark:text-gray-200
|
||||
? 'opacity-50 cursor-not-allowed pointer-events-none'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300'}"
|
||||
onclick={() => submitGuess(book.id)}
|
||||
tabindex={guessedIds.has(book.id)
|
||||
? -1
|
||||
: 0}
|
||||
>
|
||||
<span
|
||||
class="font-semibold {guessedIds.has(
|
||||
book.id,
|
||||
)
|
||||
? 'line-through text-gray-400 dark:text-gray-500'
|
||||
: ''}"
|
||||
>
|
||||
{book.name}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</li>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each sectionGroups as group (`${group.testament}:${group.section}`)}
|
||||
<li role="presentation">
|
||||
{#if group.showTestamentHeader}
|
||||
<div
|
||||
class="px-5 pt-3 pb-1 flex items-center gap-3 bg-gray-50 dark:bg-gray-700/50 border-b border-gray-100 dark:border-gray-700"
|
||||
>
|
||||
<span
|
||||
class="text-xs font-bold uppercase tracking-wider text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{group.testamentLabel}
|
||||
</span>
|
||||
<div
|
||||
class="flex-1 h-px bg-gray-200 dark:bg-gray-600"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="px-7 py-1.5 flex items-center gap-3 bg-gray-50/50 dark:bg-gray-700/30 border-b border-gray-100 dark:border-gray-700"
|
||||
>
|
||||
<span
|
||||
class="text-[11px] font-medium uppercase tracking-wider text-gray-400 dark:text-gray-500"
|
||||
>
|
||||
{group.section}
|
||||
</span>
|
||||
<div
|
||||
class="flex-1 h-px bg-gray-100 dark:bg-gray-600"
|
||||
></div>
|
||||
</div>
|
||||
<ul>
|
||||
{#each group.books as book (book.id)}
|
||||
<li
|
||||
role="option"
|
||||
aria-selected={guessedIds.has(book.id)}
|
||||
>
|
||||
<button
|
||||
class="w-full px-5 py-4 text-left border-b border-gray-100 dark:border-gray-700 last:border-b-0 flex items-center transition-all dark:text-gray-200
|
||||
{guessedIds.has(book.id)
|
||||
? 'opacity-50 cursor-not-allowed pointer-events-none'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300'}"
|
||||
onclick={() => submitGuess(book.id)}
|
||||
tabindex={guessedIds.has(book.id) ? -1 : 0}
|
||||
>
|
||||
<span
|
||||
class="font-semibold {guessedIds.has(book.id)
|
||||
? 'line-through text-gray-400 dark:text-gray-500'
|
||||
: ''}"
|
||||
>
|
||||
{book.name}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
{:else if searchQuery}
|
||||
<p class="mt-4 text-center text-gray-500 dark:text-gray-400 p-8">No books found</p>
|
||||
{/if}
|
||||
? 'opacity-50 cursor-not-allowed pointer-events-none'
|
||||
: 'hover:bg-blue-50 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300'}"
|
||||
onclick={() => submitGuess(book.id)}
|
||||
tabindex={guessedIds.has(book.id)
|
||||
? -1
|
||||
: 0}
|
||||
>
|
||||
<span
|
||||
class="font-semibold {guessedIds.has(
|
||||
book.id,
|
||||
)
|
||||
? 'line-through text-gray-400 dark:text-gray-500'
|
||||
: ''}"
|
||||
>
|
||||
{book.name}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
{:else if searchQuery}
|
||||
<p class="mt-4 text-center text-gray-500 dark:text-gray-400 p-8">
|
||||
No books found
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user