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%; + } } } }