From 998c2b692a84829cd8cdee9265c7320f9555077e Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Sat, 11 Feb 2023 11:35:28 +0000 Subject: [PATCH 1/2] :art: --- .../components/global/MkPageHeader.tabs.vue | 54 ++++++++++++------- .../src/components/global/MkPageHeader.vue | 31 ++++------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index 9b19c5dc87..dae68c7e9c 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -10,7 +10,7 @@ :class="$style.tabTitle">{{ t.title }}</div> <Transition v-else @enter="enter" @after-enter="afterEnter" @leave="leave" @after-leave="afterLeave" mode="in-out"> - <div v-if="t.key === tab" :class="$style.tabTitle">{{ t.title }}</div> + <div v-show="t.key === tab" :class="[$style.tabTitle, $style.animate]">{{ t.title }}</div> </Transition> </div> </button> @@ -34,7 +34,7 @@ export type Tab = { </script> <script lang="ts" setup> -import { onMounted, onUnmounted, watch, nextTick } from 'vue'; +import { onMounted, onUnmounted, watch, nextTick, Transition, shallowRef } from 'vue'; import { defaultStore } from '@/store'; const props = withDefaults(defineProps<{ @@ -50,9 +50,9 @@ const emit = defineEmits<{ (ev: 'tabClick', key: string); }>(); -let el = $shallowRef<HTMLElement | null>(null); +const el = shallowRef<HTMLElement | null>(null); const tabRefs: Record<string, HTMLElement | null> = {}; -let tabHighlightEl = $shallowRef<HTMLElement | null>(null); +const tabHighlightEl = shallowRef<HTMLElement | null>(null); function onTabMousedown(tab: Tab, ev: MouseEvent): void { // ユーザビリティの観点からmousedown時にはonClickは呼ばない @@ -77,13 +77,13 @@ function onTabClick(t: Tab, ev: MouseEvent): void { function renderTab() { const tabEl = props.tab ? tabRefs[props.tab] : undefined; - if (tabEl && tabHighlightEl && tabHighlightEl.parentElement) { + if (tabEl && tabHighlightEl.value && tabHighlightEl.value.parentElement) { // offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある // https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4 - const parentRect = tabHighlightEl.parentElement.getBoundingClientRect(); + const parentRect = tabHighlightEl.value.parentElement.getBoundingClientRect(); const rect = tabEl.getBoundingClientRect(); - tabHighlightEl.style.width = rect.width + 'px'; - tabHighlightEl.style.left = (rect.left - parentRect.left + tabHighlightEl.parentElement.scrollLeft) + 'px'; + tabHighlightEl.value.style.width = rect.width + 'px'; + tabHighlightEl.value.style.left = (rect.left - parentRect.left + tabHighlightEl.value.parentElement.scrollLeft) + 'px'; } } @@ -99,22 +99,32 @@ function onTabWheel(ev: WheelEvent) { return false; } -function enter(el: HTMLElement) { +let entering = false; + +async function enter(el: HTMLElement) { + entering = true; const elementWidth = el.getBoundingClientRect().width; el.style.width = '0'; - el.offsetWidth; // reflow + el.style.paddingLeft = '0'; + el.offsetWidth; // force reflow el.style.width = elementWidth + 'px'; - setTimeout(renderTab, 70); + el.style.paddingLeft = ''; + nextTick(() => { + entering = false; + }); + + setTimeout(renderTab, 170); } function afterEnter(el: HTMLElement) { - el.style.width = ''; - nextTick(renderTab); + //el.style.width = ''; } -function leave(el: HTMLElement) { +async function leave(el: HTMLElement) { const elementWidth = el.getBoundingClientRect().width; el.style.width = elementWidth + 'px'; - el.offsetWidth; // reflow + el.style.paddingLeft = ''; + el.offsetWidth; // force reflow el.style.width = '0'; + el.style.paddingLeft = '0'; } function afterLeave(el: HTMLElement) { el.style.width = ''; @@ -124,14 +134,17 @@ let ro2: ResizeObserver | null; onMounted(() => { watch([() => props.tab, () => props.tabs], () => { - nextTick(() => renderTab()); + nextTick(() => { + if (entering) return; + renderTab(); + }); }, { immediate: true, }); if (props.rootEl) { ro2 = new ResizeObserver((entries, observer) => { - if (document.body.contains(el as HTMLElement)) { + if (document.body.contains(el.value as HTMLElement)) { nextTick(() => renderTab()); } }); @@ -194,12 +207,15 @@ onUnmounted(() => { } .tabIcon+.tabTitle { - margin-left: 8px; + padding-left: 8px; } .tabTitle { overflow: hidden; - transition: width 0.15s ease-in-out; + + &.animate { + transition: width .15s linear, padding-left .15s linear; + } } .tabHighlight { diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index d39fcde1b5..6c908d07b1 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -1,10 +1,10 @@ <template> <div v-if="show" ref="el" :class="[$style.root]" :style="{ background: bg }"> <div :class="[$style.upper, { [$style.slim]: narrow, [$style.thin]: thin_ }]"> - <div v-if="narrow && props.displayMyAvatar && $i" class="_button" :class="$style.buttonsLeft" @click="openAccountMenu"> + <div v-if="!thin_ && narrow && props.displayMyAvatar && $i" class="_button" :class="$style.buttonsLeft" @click="openAccountMenu"> <MkAvatar :class="$style.avatar" :user="$i" /> </div> - <div v-else-if="narrow && !hideTitle" :class="$style.buttonsLeft" /> + <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft" /> <template v-if="metadata"> <div v-if="!hideTitle" :class="$style.titleContainer" @click="top"> @@ -21,7 +21,7 @@ </div> <XTabs v-if="!narrow || hideTitle" :class="$style.tabs" :tab="tab" @update:tab="key => emit('update:tab', key)" :tabs="tabs" :root-el="el" @tab-click="onTabClick"/> </template> - <div v-if="(narrow && !hideTitle) || (actions && actions.length > 0)" :class="$style.buttonsRight"> + <div v-if="(!thin_ && narrow && !hideTitle) || (actions && actions.length > 0)" :class="$style.buttonsRight"> <template v-for="action in actions"> <button v-tooltip.noDelay="action.text" class="_button" :class="[$style.button, { [$style.highlighted]: action.highlighted }]" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button> </template> @@ -142,6 +142,7 @@ onUnmounted(() => { .upper { --height: 50px; display: flex; + gap: var(--margin); height: var(--height); .tabs:first-child { @@ -151,12 +152,9 @@ onUnmounted(() => { padding-left: 16px; mask-image: linear-gradient(90deg, rgba(0,0,0,0), rgb(0,0,0) 16px, rgb(0,0,0) 100%); } - .tabs:last-child { + .tabs { margin-right: auto; } - .tabs:not(:last-child) { - margin-right: 0; - } &.thin { --height: 42px; @@ -170,19 +168,14 @@ onUnmounted(() => { &.slim { text-align: center; + gap: 0; + .tabs:first-child { + margin-left: 0; + } > .titleContainer { - flex: 1; margin: 0 auto; max-width: 100%; - - > *:first-child { - margin-left: auto; - } - - > *:last-child { - margin-right: auto; - } } } } @@ -198,8 +191,6 @@ onUnmounted(() => { align-items: center; min-width: var(--height); height: var(--height); - margin: 0 var(--margin); - &:empty { width: var(--height); } @@ -207,12 +198,12 @@ onUnmounted(() => { .buttonsLeft { composes: buttons; - margin-right: auto; + margin: 0 var(--margin) 0 0; } .buttonsRight { composes: buttons; - margin-left: auto; + margin: 0 0 0 var(--margin); } .avatar { From 5d02405a98a36dc726e1b6db9973d957a8ef6464 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Sat, 11 Feb 2023 13:06:54 +0000 Subject: [PATCH 2/2] :art: --- packages/frontend/src/components/global/MkPageHeader.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 6c908d07b1..803efb1690 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -248,7 +248,7 @@ onUnmounted(() => { white-space: nowrap; text-align: left; font-weight: bold; - flex-shrink: 0; + flex-shrink: 1; margin-left: 24px; }