waveterm/frontend/app/tab/tabcontent.tsx
Evan Simkowitz 2157df85de
Disable block pointer events during layout drag (#183)
This will ensure that the webview cannot capture the pointer events and
disrupt the drag functionality.

This also fixes an issue where greedy and imprecise bounds calculations
could result in thrashing of the layoutState.pendingAction field, which
could cause the placeholder to flicker.

This also fixes issues where some React Effects that were supposed to be
debounced or throttled were being invoked too much. This is because
useEffect regenerates the callback when it is run, resulting in the
debounce or throttle never taking effect. Moving the throttled or
debounced logic to a separate callback solves this.
2024-07-31 12:49:38 -07:00

96 lines
3.1 KiB
TypeScript

// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { Block, LayoutComponentModel } from "@/app/block/block";
import { getApi } from "@/store/global";
import * as services from "@/store/services";
import * as WOS from "@/store/wos";
import * as React from "react";
import { CenteredDiv } from "@/element/quickelems";
import { ContentRenderer } from "@/layout/lib/model";
import { TileLayout } from "frontend/layout/index";
import { getLayoutStateAtomForTab } from "frontend/layout/lib/layoutAtom";
import { useAtomValue } from "jotai";
import { useMemo } from "react";
import "./tabcontent.less";
const TabContent = React.memo(({ tabId }: { tabId: string }) => {
const oref = useMemo(() => WOS.makeORef("tab", tabId), [tabId]);
const loadingAtom = useMemo(() => WOS.getWaveObjectLoadingAtom(oref), [oref]);
const tabLoading = useAtomValue(loadingAtom);
const tabAtom = useMemo(() => WOS.getWaveObjectAtom<Tab>(oref), [oref]);
const layoutStateAtom = useMemo(() => getLayoutStateAtomForTab(tabId, tabAtom), [tabAtom, tabId]);
const tabData = useAtomValue(tabAtom);
const tileLayoutContents = useMemo(() => {
const renderBlock: ContentRenderer<TabLayoutData> = (
tabData: TabLayoutData,
ready: boolean,
disablePointerEvents: boolean,
onMagnifyToggle: () => void,
onClose: () => void,
dragHandleRef: React.RefObject<HTMLDivElement>
) => {
if (!tabData.blockId || !ready) {
return null;
}
const layoutModel: LayoutComponentModel = {
disablePointerEvents,
onClose,
onMagnifyToggle,
dragHandleRef,
};
return <Block key={tabData.blockId} blockId={tabData.blockId} layoutModel={layoutModel} preview={false} />;
};
function renderPreview(tabData: TabLayoutData) {
return <Block key={tabData.blockId} blockId={tabData.blockId} layoutModel={null} preview={true} />;
}
function onNodeDelete(data: TabLayoutData) {
return services.ObjectService.DeleteBlock(data.blockId);
}
return {
renderContent: renderBlock,
renderPreview: renderPreview,
tabId: tabId,
onNodeDelete: onNodeDelete,
};
}, []);
if (tabLoading) {
return (
<div className="tabcontent">
<CenteredDiv>Tab Loading</CenteredDiv>
</div>
);
}
if (!tabData) {
return (
<div className="tabcontent">
<CenteredDiv>Tab Not Found</CenteredDiv>
</div>
);
}
if (tabData?.blockids?.length == 0) {
return <div className="tabcontent tabcontent-empty"></div>;
}
return (
<div className="tabcontent">
<TileLayout
key={tabId}
contents={tileLayoutContents}
layoutTreeStateAtom={layoutStateAtom}
getCursorPoint={getApi().getCursorPoint}
/>
</div>
);
});
export { TabContent };