refactor(core): use element atom (#4026)

This commit is contained in:
Alex Yang
2023-08-29 18:59:39 -05:00
committed by GitHub
parent 591bfc3320
commit 4aabe2ea5e
10 changed files with 164 additions and 111 deletions

View File

@@ -5,112 +5,40 @@ import {
SidebarSwitch,
} from '@affine/component/app-sidebar';
import { isDesktop } from '@affine/env/constant';
import { useIsTinyScreen } from '@toeverything/hooks/use-is-tiny-screen';
import clsx from 'clsx';
import { useAtomValue } from 'jotai';
import debounce from 'lodash.debounce';
import type { MutableRefObject, ReactNode } from 'react';
import { useEffect, useRef, useState } from 'react';
import { type Atom, useAtomValue } from 'jotai';
import type { ReactElement } from 'react';
import { forwardRef, useRef } from 'react';
import * as style from './style.css';
import { TopTip } from './top-tip';
import { WindowsAppControls } from './windows-app-controls';
interface HeaderPros {
left?: ReactNode;
right?: ReactNode;
center?: ReactNode;
left?: ReactElement;
right?: ReactElement;
center?: ReactElement;
mainContainerAtom: Atom<HTMLDivElement | null>;
}
const useIsTinyScreen = ({
mainContainer,
leftStatic,
leftSlot,
centerDom,
rightStatic,
rightSlot,
}: {
mainContainer: HTMLElement;
leftStatic: MutableRefObject<HTMLElement | null>;
leftSlot: MutableRefObject<HTMLElement | null>[];
centerDom: MutableRefObject<HTMLElement | null>;
rightStatic: MutableRefObject<HTMLElement | null>;
rightSlot: MutableRefObject<HTMLElement | null>[];
}) => {
const [isTinyScreen, setIsTinyScreen] = useState(false);
useEffect(() => {
const handleResize = debounce(() => {
if (!centerDom.current) {
return;
}
const leftStaticWidth = leftStatic.current?.clientWidth || 0;
const leftSlotWidth = leftSlot.reduce((accWidth, dom) => {
return accWidth + (dom.current?.clientWidth || 0);
}, 0);
const rightStaticWidth = rightStatic.current?.clientWidth || 0;
const rightSlotWidth = rightSlot.reduce((accWidth, dom) => {
return accWidth + (dom.current?.clientWidth || 0);
}, 0);
if (!leftSlotWidth && !rightSlotWidth) {
if (isTinyScreen) {
setIsTinyScreen(false);
}
return;
}
const containerRect = mainContainer.getBoundingClientRect();
const centerRect = centerDom.current.getBoundingClientRect();
if (
leftStaticWidth + leftSlotWidth + containerRect.left >=
centerRect.left ||
containerRect.right - centerRect.right <=
rightSlotWidth + rightStaticWidth
) {
setIsTinyScreen(true);
} else {
setIsTinyScreen(false);
}
}, 100);
handleResize();
const resizeObserver = new ResizeObserver(() => {
handleResize();
});
resizeObserver.observe(mainContainer);
return () => {
resizeObserver.disconnect();
};
}, [
centerDom,
isTinyScreen,
leftSlot,
leftStatic,
mainContainer,
rightSlot,
rightStatic,
]);
return isTinyScreen;
};
// The Header component is used to solve the following problems
// 1. Manage layout issues independently of page or business logic
// 2. Dynamic centered middle element (relative to the main-container), when the middle element is detected to collide with the two elements, the line wrapping process is performed
export const Header = ({ left, center, right }: HeaderPros) => {
export const Header = forwardRef<HTMLDivElement, HeaderPros>(function Header(
{ left, center, right, mainContainerAtom },
ref
) {
const sidebarSwitchRef = useRef<HTMLDivElement | null>(null);
const leftSlotRef = useRef<HTMLDivElement | null>(null);
const centerSlotRef = useRef<HTMLDivElement | null>(null);
const rightSlotRef = useRef<HTMLDivElement | null>(null);
const windowControlsRef = useRef<HTMLDivElement | null>(null);
const mainContainer = useAtomValue(mainContainerAtom);
const isTinyScreen = useIsTinyScreen({
mainContainer: document.querySelector('.main-container') || document.body,
mainContainer,
leftStatic: sidebarSwitchRef,
leftSlot: [leftSlotRef],
centerDom: centerSlotRef,
@@ -130,6 +58,7 @@ export const Header = ({ left, center, right }: HeaderPros) => {
data-open={open}
data-sidebar-floating={appSidebarFloating}
data-testid="header"
ref={ref}
>
<div
className={clsx(style.headerSideContainer, {
@@ -175,4 +104,6 @@ export const Header = ({ left, center, right }: HeaderPros) => {
</div>
</>
);
};
});
Header.displayName = 'Header';