diff --git a/packages/frontend/src/components/MkContainer.vue b/packages/frontend/src/components/MkContainer.vue index a6372b7b6f..d03331a6eb 100644 --- a/packages/frontend/src/components/MkContainer.vue +++ b/packages/frontend/src/components/MkContainer.vue @@ -1,6 +1,6 @@ <template> -<div class="_panel" :class="[$style.root, { [$style.naked]: naked, [$style.thin]: thin, [$style.hideHeader]: !showHeader, [$style.scrollable]: scrollable, [$style.closed]: !showBody }]"> - <header v-if="showHeader" ref="header" :class="$style.header"> +<div ref="rootEl" class="_panel" :class="[$style.root, { [$style.naked]: naked, [$style.thin]: thin, [$style.hideHeader]: !showHeader, [$style.scrollable]: scrollable, [$style.closed]: !showBody }]"> + <header v-if="showHeader" ref="headerEl" :class="$style.header"> <div :class="$style.title"> <span :class="$style.titleIcon"><slot name="icon"></slot></span> <slot name="header"></slot> @@ -23,7 +23,7 @@ @leave="leave" @after-leave="afterLeave" > - <div v-show="showBody" ref="content" :class="[$style.content, { [$style.omitted]: omitted }]"> + <div v-show="showBody" ref="contentEl" :class="[$style.content, { [$style.omitted]: omitted }]"> <slot></slot> <button v-if="omitted" :class="$style.fade" class="_button" @click="() => { ignoreOmit = true; omitted = false; }"> <span :class="$style.fadeLabel">{{ i18n.ts.showMore }}</span> @@ -33,109 +33,80 @@ </div> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> +import { onMounted, ref, shallowRef, watch } from 'vue'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; -export default defineComponent({ - props: { - showHeader: { - type: Boolean, - required: false, - default: true, - }, - thin: { - type: Boolean, - required: false, - default: false, - }, - naked: { - type: Boolean, - required: false, - default: false, - }, - foldable: { - type: Boolean, - required: false, - default: false, - }, - expanded: { - type: Boolean, - required: false, - default: true, - }, - scrollable: { - type: Boolean, - required: false, - default: false, - }, - maxHeight: { - type: Number, - required: false, - default: null, - }, - }, - data() { - return { - showBody: this.expanded, - omitted: null, - ignoreOmit: false, - defaultStore, - i18n, - }; - }, - mounted() { - this.$watch('showBody', showBody => { - const headerHeight = this.showHeader ? this.$refs.header.offsetHeight : 0; - this.$el.style.minHeight = `${headerHeight}px`; - if (showBody) { - this.$el.style.flexBasis = 'auto'; - } else { - this.$el.style.flexBasis = `${headerHeight}px`; - } - }, { - immediate: true, - }); +const props = withDefaults(defineProps<{ + showHeader?: boolean; + thin?: boolean; + naked?: boolean; + foldable?: boolean; + scrollable?: boolean; + expanded?: boolean; + maxHeight?: number | null; +}>(), { + expanded: true, + showHeader: true, + maxHeight: null, +}); - this.$el.style.setProperty('--maxHeight', this.maxHeight + 'px'); +const rootEl = shallowRef<HTMLElement>(); +const contentEl = shallowRef<HTMLElement>(); +const headerEl = shallowRef<HTMLElement>(); +const showBody = ref(props.expanded); +const ignoreOmit = ref(false); +const omitted = ref(false); - const calcOmit = () => { - if (this.omitted || this.ignoreOmit || this.maxHeight == null) return; - const height = this.$refs.content.offsetHeight; - this.omitted = height > this.maxHeight; - }; +function enter(el) { + const elementHeight = el.getBoundingClientRect().height; + el.style.height = 0; + el.offsetHeight; // reflow + el.style.height = Math.min(elementHeight, props.maxHeight ?? Infinity) + 'px'; +} +function afterEnter(el) { + el.style.height = null; +} + +function leave(el) { + const elementHeight = el.getBoundingClientRect().height; + el.style.height = elementHeight + 'px'; + el.offsetHeight; // reflow + el.style.height = 0; +} + +function afterLeave(el) { + el.style.height = null; +} + +const calcOmit = () => { + if (omitted.value || ignoreOmit.value || props.maxHeight == null) return; + const height = contentEl.value.offsetHeight; + omitted.value = height > props.maxHeight; +}; + +onMounted(() => { + watch(showBody, v => { + const headerHeight = props.showHeader ? headerEl.value.offsetHeight : 0; + rootEl.value.style.minHeight = `${headerHeight}px`; + if (v) { + rootEl.value.style.flexBasis = 'auto'; + } else { + rootEl.value.style.flexBasis = `${headerHeight}px`; + } + }, { + immediate: true, + }); + + rootEl.value.style.setProperty('--maxHeight', props.maxHeight + 'px'); + + calcOmit(); + + new ResizeObserver((entries, observer) => { calcOmit(); - new ResizeObserver((entries, observer) => { - calcOmit(); - }).observe(this.$refs.content); - }, - methods: { - toggleContent(show: boolean) { - if (!this.foldable) return; - this.showBody = show; - }, - - enter(el) { - const elementHeight = el.getBoundingClientRect().height; - el.style.height = 0; - el.offsetHeight; // reflow - el.style.height = elementHeight + 'px'; - }, - afterEnter(el) { - el.style.height = null; - }, - leave(el) { - const elementHeight = el.getBoundingClientRect().height; - el.style.height = elementHeight + 'px'; - el.offsetHeight; // reflow - el.style.height = 0; - }, - afterLeave(el) { - el.style.height = null; - }, - }, + }).observe(contentEl.value); }); </script> diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index fd070a5f13..10eee6aab1 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -65,7 +65,7 @@ const getBgColor = (el: HTMLElement) => { } }; -let rootEl = $ref<HTMLElement>(); +let rootEl = $shallowRef<HTMLElement>(); let bgSame = $ref(false); let opened = $ref(props.defaultOpen); let openedAtLeastOnce = $ref(props.defaultOpen); diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue index 0f148022bf..e2d68d12c3 100644 --- a/packages/frontend/src/components/MkOmit.vue +++ b/packages/frontend/src/components/MkOmit.vue @@ -17,7 +17,7 @@ const props = withDefaults(defineProps<{ maxHeight: 200, }); -let content = $ref<HTMLElement>(); +let content = $shallowRef<HTMLElement>(); let omitted = $ref(false); let ignoreOmit = $ref(false); diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index a1ee6367a0..eaa134df25 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -17,7 +17,7 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, watch } from 'vue'; +import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, watch, shallowRef } from 'vue'; import * as os from '@/os'; const props = withDefaults(defineProps<{ @@ -39,8 +39,8 @@ const emit = defineEmits<{ (ev: 'update:modelValue', value: number): void; }>(); -const containerEl = ref<HTMLElement>(); -const thumbEl = ref<HTMLElement>(); +const containerEl = shallowRef<HTMLElement>(); +const thumbEl = shallowRef<HTMLElement>(); const rawValue = ref((props.modelValue - props.min) / (props.max - props.min)); const steppedRawValue = computed(() => { diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue index 6a507ee1ed..6ec6e3f863 100644 --- a/packages/frontend/src/pages/welcome.timeline.vue +++ b/packages/frontend/src/pages/welcome.timeline.vue @@ -33,7 +33,7 @@ import { $i } from '@/account'; let notes = $ref<Note[]>([]); let isScrolling = $ref(false); -let scrollEl = $ref<HTMLElement>(); +let scrollEl = $shallowRef<HTMLElement>(); os.apiGet('notes/featured').then(_notes => { notes = _notes;