docs: simplify the reference and tighten the verifier (#2736)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-13 17:38:50 +02:00
1 parent 6af33c7188
commit 7375ac9d80
111 files changed
+1616 -2430

No files matched your search

@@ -1,83 +1,54 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {observeResize} from '@app/features/platform/utils/SharedResizeObserver';
import {useEffect, useState} from 'react';
type Callback = (el: Element) => void;
function observeElement(el: Element, cb: Callback): () => void {
if (typeof ResizeObserver === 'undefined') return () => {};
return observeResize(el, () => cb(el));
}
import {type RefObject, useEffect, useState} from 'react';
type OverflowAxis = 'horizontal' | 'vertical' | 'both';
function getElementOverflowState(el: HTMLElement, axis: OverflowAxis): boolean {
const {scrollWidth, clientWidth, scrollHeight, clientHeight} = el;
const horizontalOverflowing = scrollWidth - clientWidth > 1;
const verticalOverflowing = scrollHeight - clientHeight > 1;
return axis === 'horizontal'
? horizontalOverflowing
: axis === 'vertical'
? verticalOverflowing
: horizontalOverflowing || verticalOverflowing;
return (
(axis !== 'vertical' && el.scrollWidth - el.clientWidth > 1) ||
(axis !== 'horizontal' && el.scrollHeight - el.clientHeight > 1)
);
}
export function useElementOverflow(element: HTMLElement | null, axis: OverflowAxis = 'horizontal'): boolean {
function useOverflow(target: HTMLElement | RefObject<HTMLElement | null> | null, axis: OverflowAxis): boolean {
const [isOverflowing, setIsOverflowing] = useState(false);
useEffect(() => {
if (!element) {
const element = target && 'current' in target ? target.current : target;
const ownerWindow = element?.ownerDocument.defaultView;
if (!element || !ownerWindow) {
setIsOverflowing(false);
return;
}
let rafId: number | null = null;
let disposed = false;
const checkOverflow = () => {
rafId = null;
const overflowing = getElementOverflowState(element, axis);
setIsOverflowing((prev) => (prev === overflowing ? prev : overflowing));
};
const scheduleCheckOverflow = () => {
if (rafId != null) return;
rafId = requestAnimationFrame(checkOverflow);
if (disposed || rafId != null) return;
rafId = ownerWindow.requestAnimationFrame(checkOverflow);
};
scheduleCheckOverflow();
const unobserve = observeElement(element, scheduleCheckOverflow);
const unobserve = typeof ResizeObserver === 'undefined' ? undefined : observeResize(element, scheduleCheckOverflow);
return () => {
disposed = true;
if (rafId != null) {
cancelAnimationFrame(rafId);
ownerWindow.cancelAnimationFrame(rafId);
}
unobserve();
unobserve?.();
};
}, [element, axis]);
}, [target, axis]);
return isOverflowing;
}
export function useTextOverflow(ref: React.RefObject<HTMLElement | null>, axis: OverflowAxis = 'horizontal'): boolean {
const [isOverflowing, setIsOverflowing] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el) {
setIsOverflowing(false);
return;
}
let rafId: number | null = null;
const checkOverflow = () => {
rafId = null;
const overflowing = getElementOverflowState(el, axis);
setIsOverflowing((prev) => (prev === overflowing ? prev : overflowing));
};
const scheduleCheckOverflow = () => {
if (rafId != null) return;
rafId = requestAnimationFrame(checkOverflow);
};
scheduleCheckOverflow();
const unobserve = observeElement(el, scheduleCheckOverflow);
return () => {
if (rafId != null) {
cancelAnimationFrame(rafId);
}
unobserve();
};
}, [ref, axis]);
return isOverflowing;
export function useElementOverflow(element: HTMLElement | null, axis: OverflowAxis = 'horizontal'): boolean {
return useOverflow(element, axis);
}
export function useTextOverflow(ref: RefObject<HTMLElement | null>, axis: OverflowAxis = 'horizontal'): boolean {
return useOverflow(ref, axis);
}