11<script setup lang="ts">
2- const TOOLTIP_RESET_MS = 2000
32const TOOLTIP_DELAY_MS = 1300
43const TOOLTIP_NEIGHBOR_RANGE = 7
54const TOOLTIP_UPPER_SIDE_OFFSET = - 85
65const TOOLTIP_LOWER_SIDE_OFFSET = - 45
76const 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-
298const options = ref <string []>([
309 ' divisions' ,
3110 ' categories' ,
@@ -34,82 +13,47 @@ const options = ref<string[]>([
3413
3514const 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-
4516const 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
10445const tooltipBoundary = ref <HTMLElement | null >(null )
10546const popoverBoundary = ref <HTMLElement | null >(null )
10647const fullscreenTarget = useTemplateRef <HTMLElement >(' fullscreenTarget' )
107- const controlsVisible = ref (true )
10848const { 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
11458function 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-
14676function 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 >
0 commit comments