firefish/packages/client/src/components/global/MkStickyContainer.vue

83 lines
1.6 KiB
Vue

<template>
<div class="sticky-container">
<div ref="headerEl">
<slot name="header"></slot>
</div>
<div ref="bodyEl" :data-sticky-container-header-height="headerHeight">
<slot></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 type { Ref } from "vue";
import { inject, onMounted, onUnmounted, provide, ref, watch } from "vue";
const headerEl = ref<HTMLElement>();
const bodyEl = ref<HTMLElement>();
const headerHeight = ref<string | undefined>();
const childStickyTop = ref(0);
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
provide(CURRENT_STICKY_TOP, childStickyTop);
const calc = () => {
childStickyTop.value = parentStickyTop.value + headerEl.value!.offsetHeight;
headerHeight.value = headerEl.value!.offsetHeight.toString();
};
const observer = new ResizeObserver(() => {
window.setTimeout(() => {
calc();
}, 100);
});
onMounted(() => {
calc();
watch(parentStickyTop, calc);
watch(
childStickyTop,
() => {
bodyEl.value!.style.setProperty(
"--stickyTop",
`${childStickyTop.value}px`,
);
},
{
immediate: true,
},
);
observer.observe(headerEl.value!);
});
onUnmounted(() => {
observer.disconnect();
});
</script>
<style lang="scss">
.sticky-container {
display: flex;
flex-direction: column;
> div:first-child {
position: sticky;
top: var(--stickyTop, 0);
z-index: 1000;
}
> div:last-child {
display: flex;
flex-direction: column;
flex-grow: 1;
}
}
</style>