refactor: Migrate ResizeObserver to composition API (no-changelog) (#9745)

This commit is contained in:
Tomi Turtiainen 2024-06-14 14:56:41 +03:00 committed by GitHub
parent 8b4a9dbced
commit 2dad9ce44c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,86 +1,63 @@
<template> <script setup lang="ts">
<div ref="root"> import { ref, onMounted, onBeforeUnmount, computed } from 'vue';
<slot :bp="bp"></slot>
</div>
</template>
<script lang="ts"> export type BreakpointDefinition = { bp: string; width: number };
import { defineComponent } from 'vue';
export default defineComponent({ const props = defineProps({
name: 'ResizeObserver',
props: {
enabled: { enabled: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
breakpoints: { breakpoints: {
type: Array, type: Array as () => BreakpointDefinition[],
validator: (bps: Array<{ bp: string; width: number }>) => { validator: (breakpoints: BreakpointDefinition[]) => {
if (breakpoints.length === 0) return true;
return breakpoints.every((bp) => typeof bp.width === 'number' && typeof bp.bp === 'string');
},
default: () => [],
},
});
const observer = ref<ResizeObserver | null>(null);
const breakpoint = ref('');
const root = ref<HTMLDivElement | null>(null);
const sortedBreakpoints = computed(() => [...props.breakpoints].sort((a, b) => a.width - b.width));
const getBreakpointFromWidth = (width: number): string => {
return ( return (
Array.isArray(bps) && sortedBreakpoints.value.find((sortedBreakpoint) => width < sortedBreakpoint.width)?.bp ??
bps.reduce( 'default'
(accu, { width, bp }) => accu && typeof width === 'number' && typeof bp === 'string',
true,
)
); );
},
},
},
data(): { observer: ResizeObserver | null; bp: string } {
return {
observer: null,
bp: '',
}; };
},
mounted() {
if (!this.enabled) {
return;
}
const root = this.$refs.root as HTMLDivElement; onMounted(() => {
if (!props.enabled) return;
if (!root.value) return;
if (!root) { breakpoint.value = getBreakpointFromWidth(root.value.offsetWidth);
return;
}
this.bp = this.getBreakpointFromWidth(root.offsetWidth); observer.value = new ResizeObserver((entries) => {
const observer = new ResizeObserver((entries) => {
entries.forEach((entry) => { entries.forEach((entry) => {
// We wrap it in requestAnimationFrame to avoid this error - ResizeObserver loop limit exceeded
requestAnimationFrame(() => { requestAnimationFrame(() => {
this.bp = this.getBreakpointFromWidth(entry.contentRect.width); breakpoint.value = getBreakpointFromWidth(entry.contentRect.width);
}); });
}); });
}); });
this.observer = observer; observer.value.observe(root.value);
observer.observe(root); });
},
beforeUnmount() {
if (this.enabled) {
this.observer?.disconnect();
}
},
methods: {
getBreakpointFromWidth(width: number): string {
let newBP = 'default';
const unsortedBreakpoints = [...(this.breakpoints || [])] as Array<{
width: number;
bp: string;
}>;
const bps = unsortedBreakpoints.sort((a, b) => a.width - b.width); onBeforeUnmount(() => {
for (let i = 0; i < bps.length; i++) { if (observer.value) {
if (width < bps[i].width) { observer.value.disconnect();
newBP = bps[i].bp;
break;
} }
}
return newBP;
},
},
}); });
</script> </script>
<template>
<div ref="root">
<slot :bp="breakpoint"></slot>
</div>
</template>