Skip to content

Commit ab32029

Browse files
committed
chore: refactor & tidy
1 parent 0e87ace commit ab32029

6 files changed

Lines changed: 255 additions & 141 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<template>
2+
<USeparator
3+
orientation="vertical"
4+
class="self-stretch mx-0.5"
5+
icon="i-material-symbols-menu-book-rounded"
6+
type="dashed"
7+
color="neutral"
8+
:ui="{
9+
icon: 'text-neutral-700 dark:text-neutral-300',
10+
border: 'border-neutral-700'
11+
}"
12+
/>
13+
</template>

app/components/bible/BibleSimpleIndex.vue

Lines changed: 60 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,10 @@
11
<script setup lang="ts">
2-
const TOOLTIP_RESET_MS = 2000
32
const TOOLTIP_DELAY_MS = 1300
43
const TOOLTIP_NEIGHBOR_RANGE = 7
54
const TOOLTIP_UPPER_SIDE_OFFSET = -85
65
const TOOLTIP_LOWER_SIDE_OFFSET = -45
76
const testamentOptions = ref<'old' | 'new' | 'both'>('both')
87
9-
type BaseCategory = 'history' | 'experience' | 'prophecy'
10-
11-
const categoryPalette: Record<BaseCategory, { solid: string, gradient: string, icon: string }> = {
12-
history: {
13-
solid: 'bg-history-700',
14-
gradient: 'bg-gradient-to-b from-history-700 from-10% to-history-500',
15-
icon: 'text-history-700'
16-
},
17-
experience: {
18-
solid: 'bg-experience-700',
19-
gradient: 'bg-gradient-to-b from-experience-700 from-10% to-experience-500',
20-
icon: 'text-experience-700'
21-
},
22-
prophecy: {
23-
solid: 'bg-prophecy-700',
24-
gradient: 'bg-gradient-to-b from-prophecy-700 from-10% to-prophecy-500',
25-
icon: 'text-prophecy-700'
26-
}
27-
}
28-
298
const options = ref<string[]>([
309
'divisions',
3110
'categories',
@@ -34,82 +13,47 @@ const options = ref<string[]>([
3413
3514
const optionSet = computed(() => new Set(options.value))
3615
37-
const { data: books } = await useAsyncData('bible-books', () => {
38-
return queryCollection('bibleBooks').all()
39-
})
40-
41-
const { data: layers } = await useAsyncData('bible-index-layers', () => {
42-
return queryCollection('bibleIndexLayers').all()
43-
})
44-
4516
const layer = ref('')
4617
47-
const layerDetails = computed(() => {
48-
return layers.value?.find(l => l.layerId === layer.value)
49-
})
50-
51-
const openBookSlugSet = computed(() => {
52-
return new Set(Object.keys(layerDetails.value?.icons ?? {}))
53-
})
54-
55-
const filteredBooks = computed(() => {
56-
return (books.value || []).filter((book) => {
57-
return testamentOptions.value === book.testament || testamentOptions.value === 'both'
58-
})
59-
})
60-
61-
const bookWidth = computed(() => filteredBooks.value ? 100 / filteredBooks.value.length : 0)
62-
63-
function getBookEnds(bookCategory: string, thisBookCategory: string) {
64-
if (optionSet.value.has('categories')) {
65-
return bookCategory !== thisBookCategory
66-
}
67-
return bookCategory?.split('-')[0] !== thisBookCategory.split('-')[0]
68-
}
69-
70-
const booksWithBookEnd = computed(() => {
71-
return (filteredBooks.value || []).map((book, index, allBooks) => {
72-
const previousBook = allBooks[index - 1]
73-
const nextBook = allBooks[index + 1]
74-
const isCategoryStart = previousBook ? getBookEnds(previousBook?.category, book.category) : true
75-
const isCategoryEnd = nextBook ? getBookEnds(nextBook?.category, book.category) : true
76-
const isLastBook = index === allBooks.length - 1
77-
78-
let bookEnd: 'start' | 'end' | 'center' | undefined = 'center'
79-
let bookAlign: typeof TOOLTIP_UPPER_SIDE_OFFSET | typeof TOOLTIP_LOWER_SIDE_OFFSET = TOOLTIP_UPPER_SIDE_OFFSET
80-
81-
if (isCategoryEnd) {
82-
bookEnd = 'end'
83-
bookAlign = isLastBook ? TOOLTIP_UPPER_SIDE_OFFSET : TOOLTIP_LOWER_SIDE_OFFSET
84-
} else if (isCategoryStart) {
85-
bookEnd = 'start'
86-
}
87-
88-
return {
89-
...book,
90-
bookEnd,
91-
bookAlign
92-
}
93-
})
94-
})
95-
96-
const tooltipIndex = ref<number>(-100)
97-
const { ready, start } = useTimeout(TOOLTIP_RESET_MS, { controls: true })
98-
watch(ready, (isReady) => {
99-
if (isReady) {
100-
tooltipIndex.value = -100
101-
}
102-
})
18+
const {
19+
books,
20+
booksWithBookEnd,
21+
bookWidth
22+
} = useBibleIndexData(
23+
testamentOptions,
24+
optionSet,
25+
TOOLTIP_UPPER_SIDE_OFFSET,
26+
TOOLTIP_LOWER_SIDE_OFFSET
27+
)
28+
29+
const {
30+
layers,
31+
layerDetails,
32+
isPopoverOpen,
33+
getLayerIcons
34+
} = useBibleLayers(layer)
35+
36+
const {
37+
tooltipIndex,
38+
handlePointerEnter,
39+
handlePointerLeave,
40+
isTooltipIdle
41+
} = useTooltipHoverState(80)
42+
43+
const { getCategoryColourClass } = useBibleCategoryStyles()
10344
10445
const tooltipBoundary = ref<HTMLElement | null>(null)
10546
const popoverBoundary = ref<HTMLElement | null>(null)
10647
const fullscreenTarget = useTemplateRef<HTMLElement>('fullscreenTarget')
107-
const controlsVisible = ref(true)
10848
const { isFullscreen, toggle: toggleFullscreen } = useFullscreen(fullscreenTarget)
10949
110-
function toggleControlsVisible() {
111-
controlsVisible.value = !controlsVisible.value
112-
}
50+
const containerClass = computed(() => {
51+
if (isFullscreen.value) {
52+
return 'w-full max-w-none min-h-screen bg-default text-default py-[12vh] px-[10vw]'
53+
}
54+
55+
return 'w-full max-w-none px-0 sm:px-0 lg:px-0'
56+
})
11357
11458
function getBookHeight(bookRank: number) {
11559
if (optionSet.value.has('bookLength')) {
@@ -129,69 +73,44 @@ function getDivisionBorderStyle(bookEnd?: 'start' | 'end' | 'center') {
12973
return 'border: none;'
13074
}
13175
132-
function getBaseCategory(category: string): BaseCategory {
133-
const [base] = category.split('-')
134-
if (base === 'history' || base === 'experience' || base === 'prophecy') {
135-
return base
136-
}
137-
return 'history'
138-
}
139-
140-
function getCategoryColourClass(category: string, useGradient: boolean) {
141-
const base = getBaseCategory(category)
142-
const shouldUseGradient = useGradient && !category.endsWith('-entry')
143-
return shouldUseGradient ? categoryPalette[base].gradient : categoryPalette[base].solid
144-
}
145-
14676
function isTooltipOpen(bookEnd: 'start' | 'end' | 'center' | undefined, index: number) {
14777
if (!optionSet.value.has('bookNames')) {
14878
return false
14979
}
15080
151-
return ((bookEnd !== 'center' && (tooltipIndex.value >= index + TOOLTIP_NEIGHBOR_RANGE || tooltipIndex.value <= index - TOOLTIP_NEIGHBOR_RANGE))
152-
|| tooltipIndex.value === index)
153-
}
154-
155-
function isPopoverOpen(bookSlug: string) {
156-
return openBookSlugSet.value.has(bookSlug)
157-
}
81+
const activeIndex = tooltipIndex.value
15882
159-
function getLayerIcons(bookSlug: string) {
160-
const iconsForBook = layerDetails.value?.icons?.[bookSlug]
161-
if (iconsForBook && iconsForBook.length > 0) {
162-
return iconsForBook
83+
if (isTooltipIdle(activeIndex)) {
84+
// Idle state: show category boundary labels as the default overview.
85+
return bookEnd !== 'center'
16386
}
16487
165-
return layerDetails.value?.defaultIcons ?? []
88+
const isCurrentBook = activeIndex === index
89+
const isCategoryBoundary = bookEnd !== 'center'
90+
const isOutsideNeighborRange = activeIndex >= index + TOOLTIP_NEIGHBOR_RANGE
91+
|| activeIndex <= index - TOOLTIP_NEIGHBOR_RANGE
92+
93+
return isCurrentBook || (isCategoryBoundary && isOutsideNeighborRange)
16694
}
16795
</script>
16896

16997
<template>
170-
<UContainer
98+
<div
17199
v-if="books"
172100
ref="fullscreenTarget"
173-
:class="isFullscreen ? 'min-h-screen bg-default text-default' : ''"
174-
:style="`${isFullscreen ? 'padding: 200px 200px;' : ''}`"
175-
:ui="{ base: 'w-full max-w-none px-0 sm:px-0 lg:px-0' }"
101+
:class="containerClass"
176102
>
177103
<div
178104
ref="popoverBoundary"
179-
:style="`${isFullscreen ? 'margin-bottom: 200px;' : ''}`"
180105
class="h-64 py-4 flex flex-col justify-end"
181106
>
182107
<div ref="tooltipBoundary" class="flex flex-row ">
183108
<template
184109
v-for="(book, index) in booksWithBookEnd"
185110
:key="book.slug"
186111
>
187-
<USeparator
112+
<BibleSeperator
188113
v-if="book.slug === 'matthew' && testamentOptions === 'new'"
189-
orientation="vertical"
190-
class="self-stretch mx-0.5"
191-
icon="i-material-symbols-menu-book-rounded"
192-
type="dashed"
193-
color="neutral"
194-
:ui="{ icon: 'text-neutral-700 dark:text-neutral-300', border: 'border-neutral-700' }"
195114
/>
196115
<div
197116
:style="`width: ${bookWidth}%;`"
@@ -239,14 +158,13 @@ function getLayerIcons(bookSlug: string) {
239158
:delay-duration="TOOLTIP_DELAY_MS"
240159
arrow
241160
:content="{
242-
align: tooltipIndex === index ? 'center' : book.bookEnd,
161+
align: tooltipIndex === index ? 'center' : (book.bookEnd || 'center'),
243162
side: 'bottom',
244-
sideOffset: tooltipIndex === index ? TOOLTIP_UPPER_SIDE_OFFSET : book.bookAlign,
163+
sideOffset: tooltipIndex === index ? TOOLTIP_UPPER_SIDE_OFFSET : (book.bookAlign ?? TOOLTIP_UPPER_SIDE_OFFSET),
245164
alignOffset: 0,
246165
collisionBoundary: tooltipBoundary,
247166
collisionPadding: 0
248167
}"
249-
:text="book.shortName || book.name"
250168
:ui="{
251169
content: 'px-1.5 text-sm text-white font-bold bg-neutral-900/70 ring-neutral-900/70',
252170
arrow: 'fill-neutral-900/70 stroke-neutral-900/70'
@@ -256,18 +174,21 @@ function getLayerIcons(bookSlug: string) {
256174
class="basis-9/12"
257175
:style="getDivisionBorderStyle(book.bookEnd)"
258176
:class="getCategoryColourClass(book.category, optionSet.has('categories'))"
259-
@pointerenter="tooltipIndex = index, start()"
177+
@pointerenter="handlePointerEnter(index)"
178+
@pointerleave="handlePointerLeave()"
260179
/>
180+
<template #content>
181+
<span
182+
@pointerenter="handlePointerEnter(index)"
183+
@pointerleave="handlePointerLeave()"
184+
>
185+
{{ book.shortName || book.name }}
186+
</span>
187+
</template>
261188
</UTooltip>
262189
</div>
263-
<USeparator
190+
<BibleSeperator
264191
v-if="book.slug === 'malachi' && testamentOptions !== 'new'"
265-
orientation="vertical"
266-
class="self-stretch mx-0.5"
267-
icon="i-material-symbols-menu-book-rounded"
268-
type="dashed"
269-
color="neutral"
270-
:ui="{ icon: 'text-neutral-700 dark:text-neutral-300', border: 'border-neutral-700' }"
271192
/>
272193
</template>
273194
</div>
@@ -278,12 +199,10 @@ function getLayerIcons(bookSlug: string) {
278199
v-model:layer="layer"
279200
:layers="layers || []"
280201
:is-fullscreen="isFullscreen"
281-
:controls-visible="controlsVisible"
282202
class="mt-10"
283203
@toggle-fullscreen="toggleFullscreen()"
284-
@toggle-controls="toggleControlsVisible"
285204
/>
286-
</UContainer>
205+
</div>
287206
</template>
288207

289208
<style>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
type BaseCategory = 'history' | 'experience' | 'prophecy'
2+
3+
const categoryPalette: Record<BaseCategory, { solid: string, gradient: string }> = {
4+
history: {
5+
solid: 'bg-history-700',
6+
gradient: 'bg-gradient-to-b from-history-700 from-10% to-history-500'
7+
},
8+
experience: {
9+
solid: 'bg-experience-700',
10+
gradient: 'bg-gradient-to-b from-experience-700 from-10% to-experience-500'
11+
},
12+
prophecy: {
13+
solid: 'bg-prophecy-700',
14+
gradient: 'bg-gradient-to-b from-prophecy-700 from-10% to-prophecy-500'
15+
}
16+
}
17+
18+
function getBaseCategory(category: string): BaseCategory {
19+
const [base] = category.split('-')
20+
if (base === 'history' || base === 'experience' || base === 'prophecy') {
21+
return base
22+
}
23+
24+
return 'history'
25+
}
26+
27+
export function useBibleCategoryStyles() {
28+
function getCategoryColourClass(category: string, useGradient: boolean) {
29+
const base = getBaseCategory(category)
30+
const shouldUseGradient = useGradient && !category.endsWith('-entry')
31+
32+
return shouldUseGradient ? categoryPalette[base].gradient : categoryPalette[base].solid
33+
}
34+
35+
return {
36+
getCategoryColourClass
37+
}
38+
}

0 commit comments

Comments
 (0)