From debd3a3e7b8215208a0fe256c259748eb7ca9e27 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 15 Oct 2025 16:53:16 +0300 Subject: [PATCH 01/46] initial nested settings impl --- .../settings_modal/tabs/appearance_tab.js | 17 +- .../settings_modal/tabs/appearance_tab.scss | 2 +- .../settings_modal/tabs/appearance_tab.vue | 575 +++++++++--------- .../settings_modal/tabs/general_tab.js | 33 +- .../settings_modal/tabs/general_tab.vue | 239 ++++---- src/components/status/status.vue | 3 +- src/i18n/en.json | 4 + 7 files changed, 465 insertions(+), 408 deletions(-) diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js index 7fbb0a5cd..56e3ea10c 100644 --- a/src/components/settings_modal/tabs/appearance_tab.js +++ b/src/components/settings_modal/tabs/appearance_tab.js @@ -1,3 +1,5 @@ +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' + import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' import IntegerSetting from '../helpers/integer_setting.vue' @@ -26,11 +28,17 @@ import { useInterfaceStore, normalizeThemeData } from 'src/stores/interface' import { library } from '@fortawesome/fontawesome-svg-core' import { - faGlobe + faGlobe, + faDashboard, + faPaintRoller, + faTableColumns } from '@fortawesome/free-solid-svg-icons' library.add( - faGlobe + faGlobe, + faPaintRoller, + faDashboard, + faTableColumns ) const AppearanceTab = { @@ -88,7 +96,8 @@ const AppearanceTab = { ProfileSettingIndicator, FontControl, Preview, - PaletteEditor + PaletteEditor, + TabSwitcher }, mounted () { useInterfaceStore().getThemeData() @@ -200,6 +209,8 @@ const AppearanceTab = { paletteDataUsed () { return useInterfaceStore().paletteDataUsed }, + instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, + instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable }, availableStyles () { return [ ...this.availableThemesV3, diff --git a/src/components/settings_modal/tabs/appearance_tab.scss b/src/components/settings_modal/tabs/appearance_tab.scss index d786cfa38..aa6bd5b45 100644 --- a/src/components/settings_modal/tabs/appearance_tab.scss +++ b/src/components/settings_modal/tabs/appearance_tab.scss @@ -160,7 +160,7 @@ .theme-preview { font-size: 1rem; // fix for firefox - width: 19rem; + width: 14rem; display: flex; flex-direction: column; align-items: center; diff --git a/src/components/settings_modal/tabs/appearance_tab.vue b/src/components/settings_modal/tabs/appearance_tab.vue index cd54e2c82..05517af0c 100644 --- a/src/components/settings_modal/tabs/appearance_tab.vue +++ b/src/components/settings_modal/tabs/appearance_tab.vue @@ -1,10 +1,287 @@ - - -
-

{{ $t('settings.background') }}

- -

{{ $t('settings.set_new_background') }}

- -
- -
- - -
-
-

{{ $t('settings.scale_and_layout') }}

-
- {{ $t("settings.style.appearance_tab_note") }} -
-
- -
  • - - {{ $t('settings.disable_sticky_headers') }} - -
  • -
  • - - {{ $t('settings.show_scrollbars') }} - -
  • -
  • - - {{ $t('settings.theme_editor_min_width') }} - -
  • - + {{ $t('settings.theme_debug') }} + + +
  • + + {{ $t('settings.force_theme_recompilation_debug') }} + +
  • + + -
    -

    {{ $t('settings.visual_tweaks') }}

    - -
    - + diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index ff6d0e477..b98b7195e 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -1,5 +1,7 @@ import { mapState } from 'vuex' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' + import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' import ScopeSelector from 'src/components/scope_selector/scope_selector.vue' @@ -9,6 +11,7 @@ import UnitSetting from '../helpers/unit_setting.vue' import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' import Select from 'src/components/select/select.vue' import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue' +import FontControl from 'src/components/font_control/font_control.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' @@ -16,11 +19,19 @@ import localeService from 'src/services/locale/locale.service.js' import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js' import { library } from '@fortawesome/fontawesome-svg-core' import { - faGlobe + faGlobe, + faMessage, + faPenAlt, + faDatabase, + faSliders } from '@fortawesome/free-solid-svg-icons' library.add( - faGlobe + faGlobe, + faMessage, + faPenAlt, + faDatabase, + faSliders ) const GeneralTab = { @@ -80,7 +91,9 @@ const GeneralTab = { InterfaceLanguageSwitcher, ProfileSettingIndicator, ScopeSelector, - Select + Select, + TabSwitcher, + FontControl }, computed: { postFormats () { @@ -99,8 +112,6 @@ const GeneralTab = { this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) } }, - instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable }, - instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, ...SharedComputedObject(), ...mapState({ blockExpirationSupported: state => state.instance.blockExpiration, @@ -137,6 +148,18 @@ const GeneralTab = { this.$store.commit('setCurrentUser', user) }) }, + updateFont (key, value) { + this.$store.dispatch('setOption', { + name: 'theme3hacks', + value: { + ...this.mergedConfig.theme3hacks, + fonts: { + ...this.mergedConfig.theme3hacks.fonts, + [key]: value + } + } + }) + }, } } diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 49fc57c79..ce48d923d 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -1,8 +1,17 @@ diff --git a/src/components/status/status.vue b/src/components/status/status.vue index ffdefb24f..91caa6a8c 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -152,12 +152,11 @@ :at="false" > - {{ status.user.name }} + {{ status.user.name }} Date: Thu, 20 Nov 2025 01:24:38 +0200 Subject: [PATCH 02/46] remove side-tabs from tab-switcher, splitting functionality into separate component --- src/components/tab_switcher/tab_switcher.jsx | 14 +-- src/components/tab_switcher/tab_switcher.scss | 98 ------------------- 2 files changed, 2 insertions(+), 110 deletions(-) diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index 02a0b47c0..0c627356f 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -31,11 +31,6 @@ export default { type: Boolean, default: false }, - sideTabBar: { - required: false, - type: Boolean, - default: false - }, bodyScrollLock: { required: false, type: Boolean, @@ -157,26 +152,21 @@ export default { return (
    - { - this.sideTabBar - ?

    {props.label}

    - : '' - } {renderSlot}
    ) }) return ( -
    +
    {tabs}
    .contents { - flex: 1 1 auto; - } - - > .tabs { - flex: 0 0 auto; - overflow: hidden auto; - flex-direction: column; - - &::after, - &::before { - flex-shrink: 0; - flex-basis: 0.5em; - content: ""; - border-right: 1px solid; - border-right-color: var(--border); - } - - &::after { - flex-grow: 1; - } - - &::before { - flex-grow: 0; - } - - .tab-wrapper { - min-width: 10em; - display: flex; - flex-direction: column; - - @media all and (width <= 800px) { - min-width: 4em; - } - - &:not(.active)::after { - top: 0; - right: 0; - bottom: 0; - border-right: 1px solid; - border-right-color: var(--border); - } - - &::before { - flex: 0 0 6px; - content: ""; - border-right: 1px solid; - border-right-color: var(--border); - } - - &:last-child .tab { - margin-bottom: 0; - } - } - - .tab { - flex: 1; - box-sizing: content-box; - max-width: 9em; - min-width: 1px; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - padding-left: 1em; - padding-right: calc(1em + 200px); - margin-right: -200px; - margin-left: 1em; - - &:not(.active) { - margin-top: 0; - margin-left: 1.5em; - } - - @media all and (width <= 800px) { - padding-left: 0.25em; - padding-right: calc(0.25em + 200px); - margin-right: calc(0.25em - 200px); - margin-left: 0.25em; - - &:not(.active) { - margin-top: 0; - margin-left: 0.5em; - } - - .text { - display: none; - } - } - } - } - } - .contents { flex: 1 0 auto; min-height: 0; From a96f5337775328f729caabf6fde3e8ea0f977c26 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Nov 2025 02:07:00 +0200 Subject: [PATCH 03/46] vertical tab switcher initial implementation --- .../settings_modal_admin_content.js | 4 +- .../settings_modal_admin_content.vue | 4 +- .../settings_modal_user_content.js | 4 +- .../settings_modal_user_content.vue | 4 +- .../settings_modal/tabs/appearance_tab.js | 4 +- .../settings_modal/tabs/appearance_tab.vue | 4 +- .../settings_modal/tabs/general_tab.js | 4 +- .../settings_modal/tabs/general_tab.vue | 6 +- .../tab_switcher/vertical_tab_switcher.jsx | 166 ++++++++++++++++++ .../tab_switcher/vertical_tab_switcher.scss | 72 ++++++++ 10 files changed, 254 insertions(+), 18 deletions(-) create mode 100644 src/components/tab_switcher/vertical_tab_switcher.jsx create mode 100644 src/components/tab_switcher/vertical_tab_switcher.scss diff --git a/src/components/settings_modal/settings_modal_admin_content.js b/src/components/settings_modal/settings_modal_admin_content.js index 593318ec4..fa6b7f8ad 100644 --- a/src/components/settings_modal/settings_modal_admin_content.js +++ b/src/components/settings_modal/settings_modal_admin_content.js @@ -1,4 +1,4 @@ -import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' +import VerticalTabSwitcher from 'src/components/tab_switcher/vertical_tab_switcher.jsx' import InstanceTab from './admin_tabs/instance_tab.vue' import LimitsTab from './admin_tabs/limits_tab.vue' @@ -31,7 +31,7 @@ library.add( const SettingsModalAdminContent = { components: { - TabSwitcher, + VerticalTabSwitcher, InstanceTab, LimitsTab, diff --git a/src/components/settings_modal/settings_modal_admin_content.vue b/src/components/settings_modal/settings_modal_admin_content.vue index 39ef74f64..501a3acf6 100644 --- a/src/components/settings_modal/settings_modal_admin_content.vue +++ b/src/components/settings_modal/settings_modal_admin_content.vue @@ -1,5 +1,5 @@ diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js index c46b477d8..ed39b30b2 100644 --- a/src/components/settings_modal/settings_modal_user_content.js +++ b/src/components/settings_modal/settings_modal_user_content.js @@ -1,4 +1,4 @@ -import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' +import VerticalTabSwitcher from 'src/components/tab_switcher/vertical_tab_switcher.jsx' import DataImportExportTab from './tabs/data_import_export_tab.vue' import MutesAndBlocksTab from './tabs/mutes_and_blocks_tab.vue' @@ -42,7 +42,7 @@ library.add( const SettingsModalContent = { components: { - TabSwitcher, + VerticalTabSwitcher, DataImportExportTab, MutesAndBlocksTab, diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue index f9a1e99bc..6bf8dc5ee 100644 --- a/src/components/settings_modal/settings_modal_user_content.vue +++ b/src/components/settings_modal/settings_modal_user_content.vue @@ -1,5 +1,5 @@ diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js index 56e3ea10c..2978142ef 100644 --- a/src/components/settings_modal/tabs/appearance_tab.js +++ b/src/components/settings_modal/tabs/appearance_tab.js @@ -1,4 +1,4 @@ -import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' +import VerticalTabSwitcher from 'src/components/tab_switcher/vertical_tab_switcher.jsx' import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' @@ -97,7 +97,7 @@ const AppearanceTab = { FontControl, Preview, PaletteEditor, - TabSwitcher + VerticalTabSwitcher }, mounted () { useInterfaceStore().getThemeData() diff --git a/src/components/settings_modal/tabs/appearance_tab.vue b/src/components/settings_modal/tabs/appearance_tab.vue index 05517af0c..9d45c3c19 100644 --- a/src/components/settings_modal/tabs/appearance_tab.vue +++ b/src/components/settings_modal/tabs/appearance_tab.vue @@ -1,5 +1,5 @@ diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index b98b7195e..68dd31941 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -1,6 +1,6 @@ import { mapState } from 'vuex' -import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' +import VerticalTabSwitcher from 'src/components/tab_switcher/vertical_tab_switcher.jsx' import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' @@ -92,7 +92,7 @@ const GeneralTab = { ProfileSettingIndicator, ScopeSelector, Select, - TabSwitcher, + VerticalTabSwitcher, FontControl }, computed: { diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index ce48d923d..3c16c5cdf 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -1,10 +1,8 @@ diff --git a/src/components/tab_switcher/vertical_tab_switcher.jsx b/src/components/tab_switcher/vertical_tab_switcher.jsx new file mode 100644 index 000000000..7182c944d --- /dev/null +++ b/src/components/tab_switcher/vertical_tab_switcher.jsx @@ -0,0 +1,166 @@ +// eslint-disable-next-line no-unused +import { h, Fragment } from 'vue' +import { mapState } from 'pinia' +import { throttle } from 'lodash' +import { mapState as mapPiniaState } from 'pinia' +import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' + +import './vertical_tab_switcher.scss' +import { useInterfaceStore } from 'src/stores/interface' + +const findFirstUsable = (slots) => slots.findIndex(_ => _.props) + +export default { + name: 'VerticalTabSwitcher', + props: { + renderOnlyFocused: { + required: false, + type: Boolean, + default: false + }, + onSwitch: { + required: false, + type: Function, + default: undefined + }, + activeTab: { + required: false, + type: String, + default: undefined + }, + bodyScrollLock: { + required: false, + type: Boolean, + default: false + } + }, + data () { + return { + active: findFirstUsable(this.slots()), + resizeHandler: null + } + }, + computed: { + activeIndex () { + // In case of controlled component + if (this.activeTab) { + return this.slots().findIndex(slot => slot && slot.props && this.activeTab === slot.props.key) + } else { + return this.active + } + }, + isActive () { + return tabName => { + const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName + return this.$slots.default().findIndex(isWanted) === this.activeIndex + } + }, + ...mapPiniaState(useInterfaceStore, { + mobileLayout: store => store.layoutType === 'mobile' + }), + }, + beforeUpdate () { + const currentSlot = this.slots()[this.active] + if (!currentSlot.props) { + this.active = findFirstUsable(this.slots()) + } + }, + methods: { + clickTab (index) { + return (e) => { + e.preventDefault() + this.setTab(index) + console.log(index) + } + }, + // DO NOT put it to computed, it doesn't work (caching?) + slots () { + if (this.$slots.default()[0].type === Fragment) { + return this.$slots.default()[0].children + } + return this.$slots.default() + }, + setTab (index) { + if (typeof this.onSwitch === 'function') { + this.onSwitch.call(null, this.slots()[index].key) + } + this.active = index + this.$refs.contents.scrollTop = 0 + } + }, + render () { + const tabs = this.slots() + .map((slot, index) => { + const props = slot.props + if (!props) return + const classesTab = ['vertical-tab menu-item'] + if (this.activeIndex === index) { + classesTab.push('-active') + } + return ( + + ) + }) + + const contents = this.slots().map((slot, index) => { + const props = slot.props + if (!props) return + const active = this.activeIndex === index + const classes = [ active ? 'active' : 'hidden' ] + if (props.fullHeight) { + classes.push('full-height') + } + let delayRender = slot.props['delay-render'] + if (delayRender && active) { + slot.props['delay-render'] = false + delayRender = false + } + const renderSlot = (!delayRender && (!this.renderOnlyFocused || active)) + ? slot + : '' + + const header = ( +

    + {props.label} +

    + ) + + return ( +
    + {header} + {renderSlot} +
    + ) + }) + + return ( +
    +
    + {tabs} +
    +
    + {contents} +
    +
    + ) + } +} diff --git a/src/components/tab_switcher/vertical_tab_switcher.scss b/src/components/tab_switcher/vertical_tab_switcher.scss new file mode 100644 index 000000000..c5454d52d --- /dev/null +++ b/src/components/tab_switcher/vertical_tab_switcher.scss @@ -0,0 +1,72 @@ +.vertical-tab-switcher { + display: flex; + flex-direction: row; + + > .tabs { + flex: 0 0 auto; + overflow: hidden auto; + flex-direction: column; + border-right: 1px solid; + border-right-color: var(--border); + min-width: 10em; + + > .menu-item { + padding: 0.5em 1em; + + .tab-icon { + vertical-align: middle; + margin-right: 0.75em; + } + } + } + + > .contents { + flex: 1 0 auto; + + .hidden { + display: none; + } + + .full-height:not(.hidden) { + height: 100%; + display: flex; + flex-direction: column; + + > *:not(.tab-content-label) { + flex: 1; + } + } + + .tab-content-label { + font-size: 1.5em; + padding: 0.5em 1em; + margin: 0; + border-bottom: 1px solid var(--border); + } + } + + > .tabs, + > .content { + transition: width 2s ease-in; + } + + &.-tabs { + > .tabs { + width: 100%; + } + + > .content { + width: 0%; + } + } + + &.-content { + > .tabs { + width: 0%; + } + + > .content { + width: 100%; + } + } +} From 5958c32acf2c79f2d7380a6f82556d43a426f088 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Nov 2025 08:07:51 +0200 Subject: [PATCH 04/46] fix turd that kept breaking UI on hot reload --- src/components/popover/popover.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index d2e605adb..e8e857918 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -299,8 +299,8 @@ const Popover = { if (this.trigger === 'click') { document.removeEventListener('click', this.onClickOutside) } - this.scrollable.removeEventListener('scroll', this.onScroll) - this.scrollable.removeEventListener('resize', this.onResize) + this.scrollable?.removeEventListener('scroll', this.onScroll) + this.scrollable?.removeEventListener('resize', this.onResize) }, resizePopover () { setTimeout(() => { From e6f025bf6ef3d8e712e7d38115836ad1c5d29bcb Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Nov 2025 12:12:14 +0200 Subject: [PATCH 05/46] after 9000 hours it finally works --- .../settings_modal_user_content.js | 6 ++ .../settings_modal_user_content.vue | 15 +++- .../settings_modal/tabs/appearance_tab.vue | 2 + .../settings_modal/tabs/general_tab.vue | 2 + src/components/tab_switcher/tab_switcher.jsx | 6 +- .../tab_switcher/vertical_tab_switcher.jsx | 60 ++++++++++--- .../tab_switcher/vertical_tab_switcher.scss | 89 +++++++++++-------- 7 files changed, 131 insertions(+), 49 deletions(-) diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js index ed39b30b2..5827c8fb3 100644 --- a/src/components/settings_modal/settings_modal_user_content.js +++ b/src/components/settings_modal/settings_modal_user_content.js @@ -88,6 +88,12 @@ const SettingsModalContent = { // Clear the state of target tab, so that next time settings is opened // it doesn't force it. useInterfaceStore().clearSettingsModalTargetTab() + }, + nestedTooBig () { + this.$refs.tabSwitcher.showNav() + }, + nestedTooSmall () { + this.$refs.tabSwitcher.hideNav() } }, mounted () { diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue index 6bf8dc5ee..8771cb5cf 100644 --- a/src/components/settings_modal/settings_modal_user_content.vue +++ b/src/components/settings_modal/settings_modal_user_content.vue @@ -2,24 +2,33 @@
    - +
    - +
    +
    {contents}
    diff --git a/src/components/tab_switcher/vertical_tab_switcher.jsx b/src/components/tab_switcher/vertical_tab_switcher.jsx index 7182c944d..d28d6d75a 100644 --- a/src/components/tab_switcher/vertical_tab_switcher.jsx +++ b/src/components/tab_switcher/vertical_tab_switcher.jsx @@ -34,10 +34,12 @@ export default { default: false } }, + emits: ['tooBig', 'tooSmall'], data () { return { active: findFirstUsable(this.slots()), - resizeHandler: null + resizeHandler: null, + navMode: false } }, computed: { @@ -59,6 +61,16 @@ export default { mobileLayout: store => store.layoutType === 'mobile' }), }, + created () { + this.resizeHandler = throttle(this.onResize, 200) + window.addEventListener('resize', this.resizeHandler) + }, + mounted () { + this.resizeHandler() + }, + unmounted () { + window.removeEventListener('resize', this.resizeHandler) + }, beforeUpdate () { const currentSlot = this.slots()[this.active] if (!currentSlot.props) { @@ -70,7 +82,20 @@ export default { return (e) => { e.preventDefault() this.setTab(index) - console.log(index) + } + }, + onResize (index) { + const tabContent = this.$refs.contents?.querySelector('.tab-content-wrapper.-active .tab-content') + const tabContentWidth = tabContent.clientWidth + const rootWidth = this.$refs.root?.clientWidth + const navWidth = this.$refs.nav?.clientWidth + const contentsWidth = this.$refs.contents?.clientWidth + + + if (contentsWidth < tabContentWidth) { + this.$emit('tooSmall') + } else if (contentsWidth - navWidth >= tabContentWidth){ + this.$emit('tooBig') } }, // DO NOT put it to computed, it doesn't work (caching?) @@ -85,7 +110,12 @@ export default { this.onSwitch.call(null, this.slots()[index].key) } this.active = index - this.$refs.contents.scrollTop = 0 + }, + showNav () { + this.navMode = false + }, + hideNav () { + this.navMode = true } }, render () { @@ -93,7 +123,7 @@ export default { .map((slot, index) => { const props = slot.props if (!props) return - const classesTab = ['vertical-tab menu-item'] + const classesTab = ['vertical-tab', 'menu-item'] if (this.activeIndex === index) { classesTab.push('-active') } @@ -104,6 +134,7 @@ export default { class={classesTab.join(' ')} type="button" role="tab" + title={props.label} > {!props.icon ? '' : ()} @@ -117,9 +148,9 @@ export default { const props = slot.props if (!props) return const active = this.activeIndex === index - const classes = [ active ? 'active' : 'hidden' ] + const classes = ['tab-content-wrapper', active ? '-active' : '-hidden' ] if (props.fullHeight) { - classes.push('full-height') + classes.push('-full-height') } let delayRender = slot.props['delay-render'] if (delayRender && active) { @@ -137,17 +168,25 @@ export default { ) return ( -
    +
    {header} - {renderSlot} +
    + {renderSlot} +
    ) }) + const rootClasses = ['vertical-tab-switcher'] + if (this.navMode) { + rootClasses.push('-nav-mode') + rootClasses.push('-nav-content') + } + return ( -
    +
    @@ -157,6 +196,7 @@ export default { role="tabpanel" class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')} v-body-scroll-lock={this.bodyScrollLock} + ref="contents" > {contents}
    diff --git a/src/components/tab_switcher/vertical_tab_switcher.scss b/src/components/tab_switcher/vertical_tab_switcher.scss index c5454d52d..7dd0c473f 100644 --- a/src/components/tab_switcher/vertical_tab_switcher.scss +++ b/src/components/tab_switcher/vertical_tab_switcher.scss @@ -4,11 +4,13 @@ > .tabs { flex: 0 0 auto; - overflow: hidden auto; flex-direction: column; + overflow: hidden auto; + white-space: nowrap; + text-overflow: ellipsis; + width: 15em; border-right: 1px solid; border-right-color: var(--border); - min-width: 10em; > .menu-item { padding: 0.5em 1em; @@ -21,52 +23,69 @@ } > .contents { - flex: 1 0 auto; + flex: 1 1 auto; + overflow-x: hidden; - .hidden { - display: none; - } + .tab-content { + align-self: center; - .full-height:not(.hidden) { - height: 100%; - display: flex; - flex-direction: column; + &:not(.-full-width) { + width: 30em; + } - > *:not(.tab-content-label) { - flex: 1; + &.-full-width { + align-self: stretch; } } - .tab-content-label { - font-size: 1.5em; - padding: 0.5em 1em; - margin: 0; - border-bottom: 1px solid var(--border); + .tab-content-wrapper { + display: flex; + flex-direction: column; + + .tab-content-label { + font-size: 1.5em; + padding: 0.5em 1em; + margin: 0; + border-bottom: 1px solid var(--border); + } + + &.-hidden { + display: none; + } + + &.-full-height:not(.-hidden) { + height: 100%; + display: flex; + flex-direction: column; + + > *:not(.tab-content-label) { + flex: 1; + } + } } } - > .tabs, - > .content { - transition: width 2s ease-in; - } + &.-nav-mode { + &.-nav-tabs { + > .tabs { + width: 100%; + } - &.-tabs { - > .tabs { - width: 100%; + > .nav-content { + width: 0%; + } } - > .content { - width: 0%; - } - } + &.-nav-content { + > .tabs { + position: absolute; + pointer-events: none; + opacity: 0; + } - &.-content { - > .tabs { - width: 0%; - } - - > .content { - width: 100%; + > .nav-content { + width: 100%; + } } } } From 8e6800fd1ece29964d5287620c441eb9e86f7cd2 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Nov 2025 18:29:09 +0200 Subject: [PATCH 06/46] level 2 collapse --- .../settings_modal_user_content.js | 7 +++++++ .../settings_modal_user_content.vue | 1 + .../settings_modal/tabs/general_tab.js | 6 ++++++ .../settings_modal/tabs/general_tab.vue | 1 + .../tab_switcher/vertical_tab_switcher.jsx | 18 +++++++++++++++--- .../tab_switcher/vertical_tab_switcher.scss | 12 ++++++++++++ 6 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js index 5827c8fb3..c770fbe04 100644 --- a/src/components/settings_modal/settings_modal_user_content.js +++ b/src/components/settings_modal/settings_modal_user_content.js @@ -73,6 +73,11 @@ const SettingsModalContent = { return useInterfaceStore().layoutType === 'mobile' } }, + data () { + return { + navCollapsed: false + } + }, methods: { onOpen () { const targetTab = useInterfaceStore().settingsModalTargetTab @@ -90,9 +95,11 @@ const SettingsModalContent = { useInterfaceStore().clearSettingsModalTargetTab() }, nestedTooBig () { + this.navCollapsed = false this.$refs.tabSwitcher.showNav() }, nestedTooSmall () { + this.navCollapsed = true this.$refs.tabSwitcher.hideNav() } }, diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue index 8771cb5cf..de89cecc8 100644 --- a/src/components/settings_modal/settings_modal_user_content.vue +++ b/src/components/settings_modal/settings_modal_user_content.vue @@ -13,6 +13,7 @@ > diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 68dd31941..28a4eac40 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -35,6 +35,12 @@ library.add( ) const GeneralTab = { + props: { + parentCollapsed: { + required: true, + type: Boolean + } + }, data () { return { subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({ diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 9ea0fb89c..de88add2d 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -3,6 +3,7 @@ :label="$t('settings.general')" ref="tabSwitcher" class="settings_tab-switcher" + :parent-collapsed="parentCollapsed" @too-small="() => $emit('tooSmall')" @too-big="() => $emit('tooBig')" > diff --git a/src/components/tab_switcher/vertical_tab_switcher.jsx b/src/components/tab_switcher/vertical_tab_switcher.jsx index d28d6d75a..45e502f4c 100644 --- a/src/components/tab_switcher/vertical_tab_switcher.jsx +++ b/src/components/tab_switcher/vertical_tab_switcher.jsx @@ -32,6 +32,11 @@ export default { required: false, type: Boolean, default: false + }, + parentCollapsed: { + required: false, + type: Boolean, + default: false } }, emits: ['tooBig', 'tooSmall'], @@ -91,11 +96,18 @@ export default { const navWidth = this.$refs.nav?.clientWidth const contentsWidth = this.$refs.contents?.clientWidth - if (contentsWidth < tabContentWidth) { - this.$emit('tooSmall') + if (this.parentCollapsed) { + this.hideNav() + } else { + this.$emit('tooSmall') + } } else if (contentsWidth - navWidth >= tabContentWidth){ - this.$emit('tooBig') + if (this.parentCollapsed) { + this.$emit('tooBig') + } else { + this.showNav() + } } }, // DO NOT put it to computed, it doesn't work (caching?) diff --git a/src/components/tab_switcher/vertical_tab_switcher.scss b/src/components/tab_switcher/vertical_tab_switcher.scss index 7dd0c473f..480f90f1a 100644 --- a/src/components/tab_switcher/vertical_tab_switcher.scss +++ b/src/components/tab_switcher/vertical_tab_switcher.scss @@ -26,6 +26,10 @@ flex: 1 1 auto; overflow-x: hidden; + .tab-content-label { + display: none + } + .tab-content { align-self: center; @@ -77,6 +81,14 @@ } &.-nav-content { + > .contents { + > .tab-content-wrapper { + > .tab-content-label { + display: block; + } + } + } + > .tabs { position: absolute; pointer-events: none; From 7d1799e9299ebd43d595fc8acdfc271679b9d265 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Nov 2025 20:10:20 +0200 Subject: [PATCH 07/46] fix order of expansion, WIP hiding headers --- .../settings_modal_user_content.js | 7 ++++- .../settings_modal_user_content.vue | 3 +++ .../settings_modal/tabs/general_tab.js | 9 +++++++ .../settings_modal/tabs/general_tab.vue | 4 +-- .../tab_switcher/vertical_tab_switcher.jsx | 27 ++++++++++++++++--- .../tab_switcher/vertical_tab_switcher.scss | 4 +++ 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js index c770fbe04..7a05adbb1 100644 --- a/src/components/settings_modal/settings_modal_user_content.js +++ b/src/components/settings_modal/settings_modal_user_content.js @@ -75,7 +75,8 @@ const SettingsModalContent = { }, data () { return { - navCollapsed: false + navCollapsed: false, + childCollapsed: false } }, methods: { @@ -96,10 +97,14 @@ const SettingsModalContent = { }, nestedTooBig () { this.navCollapsed = false + this.childCollapsed = this.$refs.generalTab.getNavMode() + console.log(this.navCollapsed, this.childCollapsed) this.$refs.tabSwitcher.showNav() }, nestedTooSmall () { this.navCollapsed = true + this.childCollapsed = this.$refs.generalTab.getNavMode() + console.log(this.navCollapsed, this.childCollapsed) this.$refs.tabSwitcher.hideNav() } }, diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue index de89cecc8..b992796ef 100644 --- a/src/components/settings_modal/settings_modal_user_content.vue +++ b/src/components/settings_modal/settings_modal_user_content.vue @@ -3,6 +3,7 @@ ref="tabSwitcher" class="settings_tab-switcher" :scrollable-tabs="true" + :child-collapsed="childCollapsed" :body-scroll-lock="bodyLock" >
    diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 28a4eac40..897999bba 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -136,6 +136,15 @@ const GeneralTab = { this.$store.dispatch('settingsSaved', { error }) }) }, + tooSmall () { + this.$emit('tooSmall') + }, + tooBig () { + this.$emit('tooBig') + }, + getNavMode () { + return this.$refs.tabSwitcher.getNavMode() + }, clearAssetCache () { this.clearCache(cacheKey) }, diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index de88add2d..2476a8d28 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -4,8 +4,8 @@ ref="tabSwitcher" class="settings_tab-switcher" :parent-collapsed="parentCollapsed" - @too-small="() => $emit('tooSmall')" - @too-big="() => $emit('tooBig')" + @too-small="tooSmall" + @too-big="tooBig" >
    = tabContentWidth){ - if (this.parentCollapsed) { + if (!this.navMode) { this.$emit('tooBig') } else { this.showNav() @@ -122,12 +128,16 @@ export default { this.onSwitch.call(null, this.slots()[index].key) } this.active = index + this.navSide = 'content' }, showNav () { this.navMode = false }, hideNav () { this.navMode = true + }, + getNavMode () { + return this.navMode } }, render () { @@ -173,8 +183,13 @@ export default { ? slot : '' + const headerClasses = ['tab-content-label'] + if (this.childCollapsed) { + headerClasses.push('-hidden') + } const header = ( -

    +

    + {props.label}

    ) @@ -192,7 +207,11 @@ export default { const rootClasses = ['vertical-tab-switcher'] if (this.navMode) { rootClasses.push('-nav-mode') - rootClasses.push('-nav-content') + if (this.navSide === 'content') { + rootClasses.push('-nav-content') + } else { + rootClasses.push('-nav-tabs') + } } return ( diff --git a/src/components/tab_switcher/vertical_tab_switcher.scss b/src/components/tab_switcher/vertical_tab_switcher.scss index 480f90f1a..2a6e1cc6a 100644 --- a/src/components/tab_switcher/vertical_tab_switcher.scss +++ b/src/components/tab_switcher/vertical_tab_switcher.scss @@ -85,6 +85,10 @@ > .tab-content-wrapper { > .tab-content-label { display: block; + + &.-hidden { + display: none; + } } } } From 3f4ad3437763f8d1269ebefd46c7947e26cfbc89 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Nov 2025 20:52:18 +0200 Subject: [PATCH 08/46] crappy implementation of hiding extra header --- .../tab_switcher/vertical_tab_switcher.jsx | 74 ++++++++++--------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/src/components/tab_switcher/vertical_tab_switcher.jsx b/src/components/tab_switcher/vertical_tab_switcher.jsx index 5b69580bc..be7c94a50 100644 --- a/src/components/tab_switcher/vertical_tab_switcher.jsx +++ b/src/components/tab_switcher/vertical_tab_switcher.jsx @@ -36,18 +36,14 @@ export default { parentCollapsed: { required: false, type: Boolean, - default: false - }, - childCollapsed: { - required: false, - type: Boolean, - default: false + default: null } }, emits: ['tooBig', 'tooSmall'], data () { return { active: findFirstUsable(this.slots()), + childCollapsed: null, resizeHandler: null, navMode: false, navSide: 'content' @@ -93,36 +89,9 @@ export default { return (e) => { e.preventDefault() this.setTab(index) + onResize() } }, - onResize (index) { - const tabContent = this.$refs.contents?.querySelector('.tab-content-wrapper.-active .tab-content') - const tabContentWidth = tabContent.clientWidth - const rootWidth = this.$refs.root?.clientWidth - const navWidth = this.$refs.nav?.clientWidth - const contentsWidth = this.$refs.contents?.clientWidth - - if (contentsWidth < tabContentWidth) { - if (this.parentCollapsed) { - this.hideNav() - } else { - this.$emit('tooSmall') - } - } else if (contentsWidth - navWidth >= tabContentWidth){ - if (!this.navMode) { - this.$emit('tooBig') - } else { - this.showNav() - } - } - }, - // DO NOT put it to computed, it doesn't work (caching?) - slots () { - if (this.$slots.default()[0].type === Fragment) { - return this.$slots.default()[0].children - } - return this.$slots.default() - }, setTab (index) { if (typeof this.onSwitch === 'function') { this.onSwitch.call(null, this.slots()[index].key) @@ -138,6 +107,43 @@ export default { }, getNavMode () { return this.navMode + }, + onResize () { + // All other tabs are hidden and their width is most likely 0 + const tabContent = this.$refs.contents?.querySelector('.tab-content-wrapper.-active .tab-content') + const tabContentWidth = tabContent.clientWidth + const navWidth = this.$refs.nav?.clientWidth + const contentsWidth = this.$refs.contents?.clientWidth + + const nestedSwitcher = this.$refs.contents?.querySelector('.vertical-tab-switcher') + if (nestedSwitcher) { + const childHeader = nestedSwitcher.querySelector('.tab-content-label') + this.childCollapsed = childHeader?.checkVisibility() + } + // if contents takes more space than its container + if (contentsWidth < tabContentWidth) { + if (this.parentCollapsed) { + this.hideNav() + } else { + this.$emit('tooSmall') + } + // If we (theoretically) have enough space to fit it in + } else if (contentsWidth - navWidth >= tabContentWidth){ + // First expand the inner layer, then outer + // if use same logic as above order will be reversed + if (!this.navMode) { + this.$emit('tooBig') + } else { + this.showNav() + } + } + }, + // DO NOT put it to computed, it doesn't work (caching?) + slots () { + if (this.$slots.default()[0].type === Fragment) { + return this.$slots.default()[0].children + } + return this.$slots.default() } }, render () { From 8b8af2889b541dcc7a18d085113eaf58e8de6595 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Nov 2025 21:17:44 +0200 Subject: [PATCH 09/46] better impl of header hiding? --- .../settings_modal_user_content.js | 10 +++---- .../settings_modal_user_content.vue | 2 ++ .../tab_switcher/vertical_tab_switcher.jsx | 26 +++++++++++-------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js index 7a05adbb1..4f6ffe4fb 100644 --- a/src/components/settings_modal/settings_modal_user_content.js +++ b/src/components/settings_modal/settings_modal_user_content.js @@ -76,7 +76,7 @@ const SettingsModalContent = { data () { return { navCollapsed: false, - childCollapsed: false + navHideHeader: false } }, methods: { @@ -97,15 +97,15 @@ const SettingsModalContent = { }, nestedTooBig () { this.navCollapsed = false - this.childCollapsed = this.$refs.generalTab.getNavMode() - console.log(this.navCollapsed, this.childCollapsed) this.$refs.tabSwitcher.showNav() }, nestedTooSmall () { this.navCollapsed = true - this.childCollapsed = this.$refs.generalTab.getNavMode() - console.log(this.navCollapsed, this.childCollapsed) this.$refs.tabSwitcher.hideNav() + }, + nestedNavSide (side) { + console.log('SWITCH') + this.navHideHeader = side === 'content' } }, mounted () { diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue index b992796ef..22271d533 100644 --- a/src/components/settings_modal/settings_modal_user_content.vue +++ b/src/components/settings_modal/settings_modal_user_content.vue @@ -5,6 +5,7 @@ :scrollable-tabs="true" :child-collapsed="childCollapsed" :body-scroll-lock="bodyLock" + :hide-header="navHideHeader" >
    diff --git a/src/components/tab_switcher/vertical_tab_switcher.jsx b/src/components/tab_switcher/vertical_tab_switcher.jsx index be7c94a50..bb3fb7305 100644 --- a/src/components/tab_switcher/vertical_tab_switcher.jsx +++ b/src/components/tab_switcher/vertical_tab_switcher.jsx @@ -37,13 +37,17 @@ export default { required: false, type: Boolean, default: null + }, + hideHeader: { + required: false, + type: Boolean, + default: null } }, - emits: ['tooBig', 'tooSmall'], + emits: ['tooBig', 'tooSmall', 'sideSwitch'], data () { return { active: findFirstUsable(this.slots()), - childCollapsed: null, resizeHandler: null, navMode: false, navSide: 'content' @@ -89,7 +93,7 @@ export default { return (e) => { e.preventDefault() this.setTab(index) - onResize() + this.onResize() } }, setTab (index) { @@ -97,7 +101,7 @@ export default { this.onSwitch.call(null, this.slots()[index].key) } this.active = index - this.navSide = 'content' + this.changeNavSide('content') }, showNav () { this.navMode = false @@ -105,6 +109,10 @@ export default { hideNav () { this.navMode = true }, + changeNavSide (side) { + this.navSide = side + this.$emit('sideSwitch', side) + }, getNavMode () { return this.navMode }, @@ -115,11 +123,6 @@ export default { const navWidth = this.$refs.nav?.clientWidth const contentsWidth = this.$refs.contents?.clientWidth - const nestedSwitcher = this.$refs.contents?.querySelector('.vertical-tab-switcher') - if (nestedSwitcher) { - const childHeader = nestedSwitcher.querySelector('.tab-content-label') - this.childCollapsed = childHeader?.checkVisibility() - } // if contents takes more space than its container if (contentsWidth < tabContentWidth) { if (this.parentCollapsed) { @@ -190,12 +193,13 @@ export default { : '' const headerClasses = ['tab-content-label'] - if (this.childCollapsed) { + console.log(this.hideHeader) + if (this.hideHeader) { headerClasses.push('-hidden') } const header = (

    - + {props.label}

    ) From 63535b1494297d615e21fc9c240047553a5910f9 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Nov 2025 21:54:52 +0200 Subject: [PATCH 10/46] less spam of events, fix nesting headers (again) --- .../settings_modal_user_content.js | 13 +++++---- .../tab_switcher/vertical_tab_switcher.jsx | 29 +++++++++++++------ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js index 4f6ffe4fb..55f2a9ce9 100644 --- a/src/components/settings_modal/settings_modal_user_content.js +++ b/src/components/settings_modal/settings_modal_user_content.js @@ -96,15 +96,18 @@ const SettingsModalContent = { useInterfaceStore().clearSettingsModalTargetTab() }, nestedTooBig () { - this.navCollapsed = false - this.$refs.tabSwitcher.showNav() + if (this.navCollapsed) { + this.navCollapsed = false + this.$refs.tabSwitcher.showNav() + } }, nestedTooSmall () { - this.navCollapsed = true - this.$refs.tabSwitcher.hideNav() + if (!this.navCollapsed) { + this.navCollapsed = true + this.$refs.tabSwitcher.hideNav() + } }, nestedNavSide (side) { - console.log('SWITCH') this.navHideHeader = side === 'content' } }, diff --git a/src/components/tab_switcher/vertical_tab_switcher.jsx b/src/components/tab_switcher/vertical_tab_switcher.jsx index bb3fb7305..9341b2d77 100644 --- a/src/components/tab_switcher/vertical_tab_switcher.jsx +++ b/src/components/tab_switcher/vertical_tab_switcher.jsx @@ -104,24 +104,35 @@ export default { this.changeNavSide('content') }, showNav () { - this.navMode = false + if (this.navMode) { + this.navMode = false + this.changeNavSide(null) + this.onResize() + } }, hideNav () { - this.navMode = true + if (!this.navMode) { + this.navMode = true + this.changeNavSide('content') + this.onResize() + } }, changeNavSide (side) { - this.navSide = side - this.$emit('sideSwitch', side) + if (this.navSide !== side) { + this.navSide = side + this.$emit('sideSwitch', side) + } }, getNavMode () { return this.navMode }, onResize () { // All other tabs are hidden and their width is most likely 0 - const tabContent = this.$refs.contents?.querySelector('.tab-content-wrapper.-active .tab-content') + const tabContent = this.$refs.contents.querySelector('.tab-content-wrapper.-active .tab-content') const tabContentWidth = tabContent.clientWidth - const navWidth = this.$refs.nav?.clientWidth - const contentsWidth = this.$refs.contents?.clientWidth + + const navWidth = this.$refs.nav.clientWidth + const contentsWidth = this.$refs.contents.clientWidth // if contents takes more space than its container if (contentsWidth < tabContentWidth) { @@ -193,8 +204,8 @@ export default { : '' const headerClasses = ['tab-content-label'] - console.log(this.hideHeader) - if (this.hideHeader) { + if (this.hideHeader === true) { + console.log('A', this.hideHeader) headerClasses.push('-hidden') } const header = ( From 9572b9704cefe1089811ebbb9929cefb451e5c0e Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 20 Nov 2025 22:06:02 +0200 Subject: [PATCH 11/46] fix logic not working on other tabs --- src/components/tab_switcher/vertical_tab_switcher.jsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/tab_switcher/vertical_tab_switcher.jsx b/src/components/tab_switcher/vertical_tab_switcher.jsx index 9341b2d77..074b90ec4 100644 --- a/src/components/tab_switcher/vertical_tab_switcher.jsx +++ b/src/components/tab_switcher/vertical_tab_switcher.jsx @@ -121,6 +121,7 @@ export default { if (this.navSide !== side) { this.navSide = side this.$emit('sideSwitch', side) + this.onResize() } }, getNavMode () { @@ -128,11 +129,13 @@ export default { }, onResize () { // All other tabs are hidden and their width is most likely 0 - const tabContent = this.$refs.contents.querySelector('.tab-content-wrapper.-active .tab-content') + const activeTab = this.$refs.root.querySelector('.tab-content-wrapper.-active') + const tabContent = activeTab.querySelector('.tab-content') const tabContentWidth = tabContent.clientWidth + const rootWidth = this.$refs.root.clientWidth const navWidth = this.$refs.nav.clientWidth - const contentsWidth = this.$refs.contents.clientWidth + const contentsWidth = rootWidth - navWidth // if contents takes more space than its container if (contentsWidth < tabContentWidth) { @@ -141,6 +144,7 @@ export default { } else { this.$emit('tooSmall') } + // FIXME wrong again?? // If we (theoretically) have enough space to fit it in } else if (contentsWidth - navWidth >= tabContentWidth){ // First expand the inner layer, then outer @@ -205,7 +209,6 @@ export default { const headerClasses = ['tab-content-label'] if (this.hideHeader === true) { - console.log('A', this.hideHeader) headerClasses.push('-hidden') } const header = ( From fba7d15a2c50137456fdc62b5baf385b066d8ca6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 24 Nov 2025 16:06:09 +0200 Subject: [PATCH 12/46] improve font control --- src/components/font_control/font_control.vue | 21 ++++++++------------ src/i18n/en.json | 7 +++++++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index 5f4ed105c..9e2c338cf 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -1,13 +1,5 @@