refactor(hooks): share floating action-bar height syncing

AuthFilesPage and ConfigPage duplicated the ResizeObserver-to-CSS-variable wiring for their bottom action bars; extract useActionBarHeightVar and use it in both.
This commit is contained in:
LTbinglingfeng
2026-06-13 02:30:52 +08:00
Unverified
parent a984ce6ab7
commit c63c43c3a4
3 changed files with 46 additions and 50 deletions
+38
View File
@@ -0,0 +1,38 @@
import { useLayoutEffect, type RefObject } from 'react';
/**
* 将悬浮操作条的实时高度同步到根元素 CSS 变量,供页面底部留白使用。
* active 为 false 或元素未挂载时清除变量。
*/
export function useActionBarHeightVar(
ref: RefObject<HTMLElement | null>,
cssVar: string,
active: boolean
) {
useLayoutEffect(() => {
if (typeof window === 'undefined') return;
const actionsEl = active ? ref.current : null;
if (!actionsEl) {
document.documentElement.style.removeProperty(cssVar);
return;
}
const updateHeight = () => {
const height = actionsEl.getBoundingClientRect().height;
document.documentElement.style.setProperty(cssVar, `${height}px`);
};
updateHeight();
window.addEventListener('resize', updateHeight);
const ro = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(updateHeight);
ro?.observe(actionsEl);
return () => {
ro?.disconnect();
window.removeEventListener('resize', updateHeight);
document.documentElement.style.removeProperty(cssVar);
};
}, [ref, cssVar, active]);
}
+6 -26
View File
@@ -15,6 +15,7 @@ import { animate } from 'motion/mini';
import type { AnimationPlaybackControlsWithThen } from 'motion-dom';
import { useInterval } from '@/hooks/useInterval';
import { useHeaderRefresh } from '@/hooks/useHeaderRefresh';
import { useActionBarHeightVar } from '@/hooks/useActionBarHeightVar';
import { usePageTransitionLayer } from '@/components/common/PageTransitionLayer';
import { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
@@ -492,32 +493,11 @@ export function AuthFilesPage() {
[filter, navigate]
);
useLayoutEffect(() => {
if (typeof window === 'undefined') return;
const actionsEl = floatingBatchActionsRef.current;
if (!actionsEl) {
document.documentElement.style.removeProperty('--auth-files-action-bar-height');
return;
}
const updatePadding = () => {
const height = actionsEl.getBoundingClientRect().height;
document.documentElement.style.setProperty('--auth-files-action-bar-height', `${height}px`);
};
updatePadding();
window.addEventListener('resize', updatePadding);
const ro = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(updatePadding);
ro?.observe(actionsEl);
return () => {
ro?.disconnect();
window.removeEventListener('resize', updatePadding);
document.documentElement.style.removeProperty('--auth-files-action-bar-height');
};
}, [batchActionBarVisible, selectionCount]);
useActionBarHeightVar(
floatingBatchActionsRef,
'--auth-files-action-bar-height',
batchActionBarVisible
);
useEffect(() => {
selectionCountRef.current = selectionCount;
+2 -24
View File
@@ -3,7 +3,6 @@ import {
lazy,
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
@@ -25,6 +24,7 @@ import {
import { VisualConfigEditor } from '@/components/config/VisualConfigEditor';
import { DiffModal } from '@/components/config/DiffModal';
import { useMediaQuery } from '@/hooks/useMediaQuery';
import { useActionBarHeightVar } from '@/hooks/useActionBarHeightVar';
import { useUnsavedChangesGuard } from '@/hooks/useUnsavedChangesGuard';
import { useVisualConfig } from '@/hooks/useVisualConfig';
import { useNotificationStore, useAuthStore, useThemeStore, useConfigStore } from '@/stores';
@@ -427,29 +427,7 @@ export function ConfigPage() {
}, [lastSearchedQuery, performSearch]);
// Keep bottom floating actions from covering page content by syncing its height to a CSS variable.
useLayoutEffect(() => {
if (typeof window === 'undefined' || !shouldRenderFloatingActions) return;
const actionsEl = floatingActionsRef.current;
if (!actionsEl) return;
const updatePadding = () => {
const height = actionsEl.getBoundingClientRect().height;
document.documentElement.style.setProperty('--config-action-bar-height', `${height}px`);
};
updatePadding();
window.addEventListener('resize', updatePadding);
const ro = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(updatePadding);
ro?.observe(actionsEl);
return () => {
ro?.disconnect();
window.removeEventListener('resize', updatePadding);
document.documentElement.style.removeProperty('--config-action-bar-height');
};
}, [shouldRenderFloatingActions]);
useActionBarHeightVar(floatingActionsRef, '--config-action-bar-height', shouldRenderFloatingActions);
// Status text
const getStatusText = () => {