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