Implement dynamic scroll-based underlining of active section

This commit is contained in:
Jason Henriquez 2024-04-26 17:58:56 -05:00
parent f4d8648607
commit 8135ff2dd0
5 changed files with 107 additions and 45 deletions

View File

@ -2,11 +2,10 @@
position: fixed; position: fixed;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0; gap: 2px;
gap: 5px;
font-size: 22px; font-size: 22px;
inline-size: fit-content; inline-size: 250px;
max-inline-size: 250px; padding-inline-start: 10px;
} }
.header { .header {
@ -26,9 +25,29 @@
color: var(--primary-text-color); color: var(--primary-text-color);
} }
.settingsMenuTitleContent {
inline-size: fit-content;
}
.settingsMenuTitleAndIcon {
/* needed to have underline poke out */
margin-inline-start: 3px;
}
.settingsMenuTitleUnderline {
/* have underline poke out */
inline-size: calc(100% + 3px);
/* prevent "active" border from visibly pushing the content up */
border-block-end: 3px solid transparent;
}
.settingsMenuTitle.active { .settingsMenuTitle.active {
color: var(--primary-text-color); color: var(--primary-text-color);
border-block-end: 3px solid var(--primary-text-color); }
.settingsMenuTitle.active .settingsMenuTitleUnderline {
border-block-end: 3px solid var(--primary-color);
} }
.settingsMenuTitleIcon { .settingsMenuTitleIcon {

View File

@ -7,15 +7,21 @@
</h2> </h2>
<a <a
v-for="(settingsSection) in settingsSections" v-for="(settingsSection) in settingsSections"
:key="settingsSection.type + 'link'" :id="settingsSection.type + '-link'"
:key="settingsSection.type + '-link'"
:href="'#' + settingsSection.type" :href="'#' + settingsSection.type"
class="settingsMenuTitle" class="settingsMenuTitle"
> >
<div class="settingsMenuTitleContent">
<div class="settingsMenuTitleAndIcon">
<font-awesome-icon <font-awesome-icon
:icon="['fas', settingsSection.icon]" :icon="['fas', settingsSection.icon]"
class="settingsMenuTitleIcon" class="settingsMenuTitleIcon"
/> />
{{ getTitleForSection(settingsSection) }} {{ getTitleForSection(settingsSection) }}
</div>
<div class="settingsMenuTitleUnderline" />
</div>
</a> </a>
</menu> </menu>
</template> </template>

View File

@ -7,25 +7,42 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
max-inline-size: 80%;
} }
.settingsSections { .settingsSections {
margin-inline: auto; margin-inline: auto;
} }
.switchRow { .settingsSections:last-child {
display: flex; margin-block-end: calc(76vh - 150px)
} }
/* .settingsSections { .switchRow {
display: flex;
} */ margin-inline-end: auto;
}
.settingsToggle { .settingsToggle {
padding-block: 0; padding-block: 0;
margin-block: 10px 5px; margin-block: 0;
}
.section {
/* enables anchor link clicks to land right at the section headings */
scroll-margin-top: 24vh;
} }
.section + .section { .section + .section {
padding-block-start: 20px; padding-block-start: 20px;
} }
@media only screen and (width <= 950px) {
ft-settings-menu {
display: none;
}
.settingsContent {
margin-inline: auto;
}
}

View File

@ -45,10 +45,6 @@ export default defineComponent({
} }
}, },
computed: { computed: {
locale: function() {
return this.$i18n.locale.replace('_', '-')
},
settingsPassword: function () { settingsPassword: function () {
return this.$store.getters.getSettingsPassword return this.$store.getters.getSettingsPassword
}, },
@ -170,15 +166,39 @@ export default defineComponent({
return settingsSections return settingsSections
}, },
}, },
watch: {
locale: 'scrollToGeneralSettings'
},
created: function () { created: function () {
if (this.settingsPassword === '') { if (this.settingsPassword === '') {
this.unlocked = true this.unlocked = true
} }
}, },
mounted: function () {
document.addEventListener('scroll', this.scrollCurrentSection)
},
beforeDestroy: function () {
document.removeEventListener('scroll', this.scrollCurrentSection)
},
methods: { methods: {
scrollToSection(section) { scrollToGeneralSettings() {
const sectionElement = this.$refs[section] document.getElementById('general-settings')?.scrollIntoView()
sectionElement?.scrollIntoView() },
scrollCurrentSection: function() {
const scrollY = window.scrollY + innerHeight / 4
this.settingsSectionComponents.forEach((section) => {
const sectionElement = document.getElementById(section.type)
const sectionHeight = sectionElement.offsetHeight
const sectionTop = sectionElement.offsetTop
const menuElement = document.getElementById(`${section.type}-link`)
if (scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) {
menuElement.classList.add('active')
} else {
menuElement.classList.remove('active')
}
})
}, },
...mapActions([ ...mapActions([

View File

@ -17,12 +17,12 @@
<div class="settingsSections"> <div class="settingsSections">
<template <template
v-for="(settingsComponent) in settingsSectionComponents" v-for="(settingsComponent) in settingsSectionComponents"
ref=""
> >
<component <component
:is="settingsComponent.type" :is="settingsComponent.type"
:id="settingsComponent.type" :id="settingsComponent.type"
:ref="settingsComponent.type" :key="settingsComponent.type + '-component'"
:key="settingsComponent.type + 'component'"
class="section" class="section"
/> />
</template> </template>