diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index b87d46ee94..bb1269562d 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -289,12 +289,12 @@ function renote(viaKeyboard = false) { icon: 'ti ti-repeat', action: () => { const el = renoteButton.value as HTMLElement | null | undefined; - if (el) { - const rect = el.getBoundingClientRect(); - const x = rect.left + (el.offsetWidth / 2); - const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); - } + if (el) { + const rect = el.getBoundingClientRect(); + const x = rect.left + (el.offsetWidth / 2); + const y = rect.top + (el.offsetHeight / 2); + os.popup(MkRippleEffect, { x, y }, {}, 'end'); + } os.api('notes/create', { renoteId: appearNote.id, @@ -622,9 +622,9 @@ function showReactions(): void { .showLess { width: 100%; - margin-top: 1em; + margin-top: 14px; position: sticky; - bottom: 1em; + bottom: calc(var(--stickyBottom, 0px) + 14px); } .showLessLabel { diff --git a/packages/frontend/src/components/global/MkStickyContainer.vue b/packages/frontend/src/components/global/MkStickyContainer.vue index a3fee91a36..3f9c1bc353 100644 --- a/packages/frontend/src/components/global/MkStickyContainer.vue +++ b/packages/frontend/src/components/global/MkStickyContainer.vue @@ -6,20 +6,19 @@ <div ref="bodyEl" :data-sticky-container-header-height="headerHeight"> <slot></slot> </div> + <div ref="footerEl"> + <slot name="footer"></slot> + </div> </div> </template> -<script lang="ts"> -// なんか動かない -//const CURRENT_STICKY_TOP = Symbol('CURRENT_STICKY_TOP'); -const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP'; -</script> - <script lang="ts" setup> import { onMounted, onUnmounted, provide, inject, Ref, ref, watch } from 'vue'; +import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const'; const rootEl = $shallowRef<HTMLElement>(); const headerEl = $shallowRef<HTMLElement>(); +const footerEl = $shallowRef<HTMLElement>(); const bodyEl = $shallowRef<HTMLElement>(); let headerHeight = $ref<string | undefined>(); @@ -27,9 +26,17 @@ let childStickyTop = $ref(0); const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0)); provide(CURRENT_STICKY_TOP, $$(childStickyTop)); +let footerHeight = $ref<string | undefined>(); +let childStickyBottom = $ref(0); +const parentStickyBottom = inject<Ref<number>>(CURRENT_STICKY_BOTTOM, ref(0)); +provide(CURRENT_STICKY_BOTTOM, $$(childStickyBottom)); + const calc = () => { childStickyTop = parentStickyTop.value + headerEl.offsetHeight; headerHeight = headerEl.offsetHeight.toString(); + + childStickyBottom = parentStickyBottom.value + footerEl.offsetHeight; + footerHeight = footerEl.offsetHeight.toString(); }; const observer = new ResizeObserver(() => { @@ -41,7 +48,7 @@ const observer = new ResizeObserver(() => { onMounted(() => { calc(); - watch(parentStickyTop, calc); + watch([parentStickyTop, parentStickyBottom], calc); watch($$(childStickyTop), () => { bodyEl.style.setProperty('--stickyTop', `${childStickyTop}px`); @@ -49,11 +56,22 @@ onMounted(() => { immediate: true, }); + watch($$(childStickyBottom), () => { + bodyEl.style.setProperty('--stickyBottom', `${childStickyBottom}px`); + }, { + immediate: true, + }); + headerEl.style.position = 'sticky'; headerEl.style.top = 'var(--stickyTop, 0)'; headerEl.style.zIndex = '1000'; + footerEl.style.position = 'sticky'; + footerEl.style.bottom = 'var(--stickyBottom, 0)'; + footerEl.style.zIndex = '1000'; + observer.observe(headerEl); + observer.observe(footerEl); }); onUnmounted(() => { diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 1d44786a63..da3ae89c37 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -46,3 +46,9 @@ https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const; export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; + +// なんか動かない +//export const CURRENT_STICKY_TOP = Symbol('CURRENT_STICKY_TOP'); +//export const CURRENT_STICKY_BOTTOM = Symbol('CURRENT_STICKY_BOTTOM'); +export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP'; +export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM'; diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index beae799f5c..eef423bdd9 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -18,7 +18,7 @@ <button v-if="!isDesktop && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"><i class="ti ti-apps"></i></button> - <div v-if="isMobile" :class="$style.nav"> + <div v-if="isMobile" ref="navFooter" :class="$style.nav"> <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator"><i class="_indicatorCircle"></i></span></button> <button :class="$style.navButton" class="_button" @click="mainRouter.currentRoute.value.name === 'index' ? top() : mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button> <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"><i :class="$style.navButtonIcon" class="ti ti-bell"></i><span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator"><i class="_indicatorCircle"></i></span></button> @@ -84,7 +84,7 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, provide, onMounted, computed, ref, ComputedRef } from 'vue'; +import { defineAsyncComponent, provide, onMounted, computed, ref, ComputedRef, watch, inject, Ref } from 'vue'; import XCommon from './_common_/common.vue'; import { instanceName } from '@/config'; import { StickySidebar } from '@/scripts/sticky-sidebar'; @@ -98,6 +98,7 @@ import { mainRouter } from '@/router'; import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; import { deviceKind } from '@/scripts/device-kind'; import { miLocalStorage } from '@/local-storage'; +import { CURRENT_STICKY_BOTTOM } from '@/const'; const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue')); const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); @@ -115,6 +116,7 @@ window.addEventListener('resize', () => { let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); const widgetsEl = $shallowRef<HTMLElement>(); const widgetsShowing = $ref(false); +const navFooter = $shallowRef<HTMLElement>(); provide('router', mainRouter); provideMetadataReceiver((info) => { @@ -207,6 +209,21 @@ function top() { } const wallpaper = miLocalStorage.getItem('wallpaper') != null; + +let navFooterHeight = $ref(0); +provide<Ref<number>>(CURRENT_STICKY_BOTTOM, $$(navFooterHeight)); + +watch($$(navFooter), () => { + if (navFooter) { + navFooterHeight = navFooter.offsetHeight; + document.body.style.setProperty('--stickyBottom', `${navFooterHeight}px`); + } else { + navFooterHeight = 0; + document.body.style.setProperty('--stickyBottom', '0px'); + } +}, { + immediate: true, +}); </script> <style lang="scss" module> @@ -342,8 +359,8 @@ $widgets-hide-threshold: 1090px; grid-gap: 8px; width: 100%; box-sizing: border-box; - -webkit-backdrop-filter: var(--blur, blur(32px)); - backdrop-filter: var(--blur, blur(32px)); + -webkit-backdrop-filter: var(--blur, blur(24px)); + backdrop-filter: var(--blur, blur(24px)); background-color: var(--header); border-top: solid 0.5px var(--divider); }