diff options
Diffstat (limited to 'packages/frontend/src/components/global')
| -rw-r--r-- | packages/frontend/src/components/global/MkPageHeader.tabs.vue | 70 |
1 files changed, 52 insertions, 18 deletions
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index a1b57f30d9..1ef75281fd 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -4,12 +4,20 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div ref="el" :class="$style.tabs" @wheel="onTabWheel"> +<div ref="el" :class="$style.tabs" :style="{ '--tabAnchorName': tabAnchorName }" @wheel="onTabWheel"> <div :class="$style.tabsInner"> <button - v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title" - class="_button" :class="[$style.tab, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: prefer.s.animation }]" - @mousedown="(ev) => onTabMousedown(t, ev)" @click="(ev) => onTabClick(t, ev)" + v-for="t in tabs" + :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" + v-tooltip.noDelay="t.title" + class="_button" + :class="[$style.tab, { + [$style.active]: t.key != null && t.key === props.tab, + [$style.animate]: prefer.s.animation + }]" + :style="getTabStyle(t)" + @mousedown="(ev) => onTabMousedown(t, ev)" + @click="(ev) => onTabClick(t, ev)" > <div :class="$style.tabInner"> <i v-if="t.icon" :class="[$style.tabIcon, t.icon]"></i> @@ -48,6 +56,10 @@ export type Tab = { <script lang="ts" setup> import { nextTick, onMounted, onUnmounted, useTemplateRef, watch } from 'vue'; import { prefer } from '@/preferences.js'; +import { genId } from '@/utility/id.js'; + +const cssAnchorSupported = CSS.supports('position-anchor', '--anchor-name'); +const tabAnchorName = `--${genId()}-currentTab`; const props = withDefaults(defineProps<{ tabs?: Tab[]; @@ -66,6 +78,17 @@ const el = useTemplateRef('el'); const tabHighlightEl = useTemplateRef('tabHighlightEl'); const tabRefs: Record<string, HTMLElement | null> = {}; +function getTabStyle(t: Tab) { + if (!cssAnchorSupported) return {}; + if (t.key === props.tab) { + return { + anchorName: tabAnchorName, + }; + } else { + return {}; + } +} + function onTabMousedown(tab: Tab, ev: MouseEvent): void { // ユーザビリティの観点からmousedown時にはonClickは呼ばない if (tab.key) { @@ -88,6 +111,8 @@ function onTabClick(t: Tab, ev: MouseEvent): void { } function renderTab() { + if (cssAnchorSupported) return; + const tabEl = props.tab ? tabRefs[props.tab] : undefined; if (tabEl && tabHighlightEl.value && tabHighlightEl.value.parentElement) { // offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある @@ -152,22 +177,24 @@ function afterLeave(el: Element) { let ro2: ResizeObserver | null; onMounted(() => { - watch([() => props.tab, () => props.tabs], () => { - nextTick(() => { - if (entering) return; - renderTab(); + if (!cssAnchorSupported) { + watch([() => props.tab, () => props.tabs], () => { + nextTick(() => { + if (entering) return; + renderTab(); + }); + }, { + immediate: true, }); - }, { - immediate: true, - }); - if (props.rootEl) { - ro2 = new ResizeObserver((entries, observer) => { - if (window.document.body.contains(el.value as HTMLElement)) { - nextTick(() => renderTab()); - } - }); - ro2.observe(props.rootEl); + if (props.rootEl) { + ro2 = new ResizeObserver(() => { + if (window.document.body.contains(el.value as HTMLElement)) { + nextTick(() => renderTab()); + } + }); + ro2.observe(props.rootEl); + } } }); @@ -246,4 +273,11 @@ onUnmounted(() => { transition: width 0.15s ease, left 0.15s ease; } } + +@supports (position-anchor: --anchor-name) { + .tabHighlight { + left: anchor(var(--tabAnchorName) start); + width: anchor-size(var(--tabAnchorName) width); + } +} </style> |