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;
display: flex;
flex-direction: column;
padding: 0;
gap: 5px;
gap: 2px;
font-size: 22px;
inline-size: fit-content;
max-inline-size: 250px;
inline-size: 250px;
padding-inline-start: 10px;
}
.header {
@ -26,9 +25,29 @@
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 {
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 {

View File

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

View File

@ -7,25 +7,42 @@
display: flex;
flex-direction: column;
align-items: center;
max-inline-size: 80%;
}
.settingsSections {
margin-inline: auto;
}
.switchRow {
display: flex;
.settingsSections:last-child {
margin-block-end: calc(76vh - 150px)
}
/* .settingsSections {
} */
.switchRow {
display: flex;
margin-inline-end: auto;
}
.settingsToggle {
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 {
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: {
locale: function() {
return this.$i18n.locale.replace('_', '-')
},
settingsPassword: function () {
return this.$store.getters.getSettingsPassword
},
@ -170,15 +166,39 @@ export default defineComponent({
return settingsSections
},
},
watch: {
locale: 'scrollToGeneralSettings'
},
created: function () {
if (this.settingsPassword === '') {
this.unlocked = true
}
},
mounted: function () {
document.addEventListener('scroll', this.scrollCurrentSection)
},
beforeDestroy: function () {
document.removeEventListener('scroll', this.scrollCurrentSection)
},
methods: {
scrollToSection(section) {
const sectionElement = this.$refs[section]
sectionElement?.scrollIntoView()
scrollToGeneralSettings() {
document.getElementById('general-settings')?.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([

View File

@ -5,29 +5,29 @@
:settings-sections="settingsSectionComponents"
/>
<div class="settingsContent">
<div class="switchRow">
<ft-toggle-switch
class="settingsToggle"
:label="$t('Settings.Sort Settings Sections (A-Z)')"
:default-value="settingsSectionSortEnabled"
:compact="false"
@change="updateSettingsSectionSortEnabled"
/>
</div>
<div class="settingsSections">
<template
v-for="(settingsComponent) in settingsSectionComponents"
>
<component
:is="settingsComponent.type"
:id="settingsComponent.type"
:ref="settingsComponent.type"
:key="settingsComponent.type + 'component'"
class="section"
<div class="switchRow">
<ft-toggle-switch
class="settingsToggle"
:label="$t('Settings.Sort Settings Sections (A-Z)')"
:default-value="settingsSectionSortEnabled"
:compact="false"
@change="updateSettingsSectionSortEnabled"
/>
</template>
</div>
<div class="settingsSections">
<template
v-for="(settingsComponent) in settingsSectionComponents"
ref=""
>
<component
:is="settingsComponent.type"
:id="settingsComponent.type"
:key="settingsComponent.type + '-component'"
class="section"
/>
</template>
</div>
</div>
</div>
</template>
<password-dialog
v-else