mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
Unified node model to pass data from layout to blocks (#259)
This adds a new NodeModel, which can be passed from the TileLayout to contained blocks. It contains all the layout data that the block should care about, including focus status, whether a drag operation is underway, whether the node is magnified, etc. This also adds a focus stack for the layout, which will let the focus switch to the last-focused node when the currently-focused one is closed. This also addresses a regression in the resize handles that caused them to be offset from the cursor when dragged. --------- Co-authored-by: sawka <mike.sawka@gmail.com>
This commit is contained in:
@@ -1,20 +1,13 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { BlockComponentModel, BlockProps, LayoutComponentModel } from "@/app/block/blocktypes";
|
||||
import { BlockComponentModel, BlockProps } from "@/app/block/blocktypes";
|
||||
import { PlotView } from "@/app/view/plotview/plotview";
|
||||
import { PreviewModel, PreviewView, makePreviewModel } from "@/app/view/preview/preview";
|
||||
import { ErrorBoundary } from "@/element/errorboundary";
|
||||
import { CenteredDiv } from "@/element/quickelems";
|
||||
import {
|
||||
atoms,
|
||||
counterInc,
|
||||
getViewModel,
|
||||
registerViewModel,
|
||||
setBlockFocus,
|
||||
unregisterViewModel,
|
||||
useBlockAtom,
|
||||
} from "@/store/global";
|
||||
import { NodeModel } from "@/layout/index";
|
||||
import { counterInc, getViewModel, registerViewModel, unregisterViewModel } from "@/store/global";
|
||||
import * as WOS from "@/store/wos";
|
||||
import * as util from "@/util/util";
|
||||
import { CpuPlotView, CpuPlotViewModel, makeCpuPlotViewModel } from "@/view/cpuplot/cpuplot";
|
||||
@@ -24,15 +17,13 @@ import { WaveAi, WaveAiModel, makeWaveAiViewModel } from "@/view/waveai/waveai";
|
||||
import { WebView, WebViewModel, makeWebViewModel } from "@/view/webview/webview";
|
||||
import * as jotai from "jotai";
|
||||
import * as React from "react";
|
||||
import "./block.less";
|
||||
import { BlockFrame } from "./blockframe";
|
||||
import { blockViewToIcon, blockViewToName } from "./blockutil";
|
||||
|
||||
import "./block.less";
|
||||
|
||||
type FullBlockProps = {
|
||||
blockId: string;
|
||||
preview: boolean;
|
||||
layoutModel: LayoutComponentModel;
|
||||
nodeModel: NodeModel;
|
||||
viewModel: ViewModel;
|
||||
};
|
||||
|
||||
@@ -105,16 +96,15 @@ function makeDefaultViewModel(blockId: string, viewType: string): ViewModel {
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
const BlockPreview = React.memo(({ blockId, layoutModel, viewModel }: FullBlockProps) => {
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||
const BlockPreview = React.memo(({ nodeModel, viewModel }: FullBlockProps) => {
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", nodeModel.blockId));
|
||||
if (!blockData) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<BlockFrame
|
||||
key={blockId}
|
||||
blockId={blockId}
|
||||
layoutModel={layoutModel}
|
||||
key={nodeModel.blockId}
|
||||
nodeModel={nodeModel}
|
||||
preview={true}
|
||||
blockModel={null}
|
||||
viewModel={viewModel}
|
||||
@@ -122,20 +112,16 @@ const BlockPreview = React.memo(({ blockId, layoutModel, viewModel }: FullBlockP
|
||||
);
|
||||
});
|
||||
|
||||
const BlockFull = React.memo(({ blockId, layoutModel, viewModel }: FullBlockProps) => {
|
||||
const BlockFull = React.memo(({ nodeModel, viewModel }: FullBlockProps) => {
|
||||
counterInc("render-BlockFull");
|
||||
const focusElemRef = React.useRef<HTMLInputElement>(null);
|
||||
const blockRef = React.useRef<HTMLDivElement>(null);
|
||||
const [blockClicked, setBlockClicked] = React.useState(false);
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", nodeModel.blockId));
|
||||
const [focusedChild, setFocusedChild] = React.useState(null);
|
||||
const isFocusedAtom = useBlockAtom<boolean>(blockId, "isFocused", () => {
|
||||
return jotai.atom((get) => {
|
||||
const winData = get(atoms.waveWindow);
|
||||
return winData.activeblockid === blockId;
|
||||
});
|
||||
});
|
||||
const isFocused = jotai.useAtomValue(isFocusedAtom);
|
||||
const isFocused = jotai.useAtomValue(nodeModel.isFocused);
|
||||
const disablePointerEvents = jotai.useAtomValue(nodeModel.disablePointerEvents);
|
||||
const addlProps = jotai.useAtomValue(nodeModel.additionalProps);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
setBlockClicked(isFocused);
|
||||
@@ -150,24 +136,24 @@ const BlockFull = React.memo(({ blockId, layoutModel, viewModel }: FullBlockProp
|
||||
if (!focusWithin) {
|
||||
setFocusTarget();
|
||||
}
|
||||
setBlockFocus(blockId);
|
||||
nodeModel.focusNode();
|
||||
}, [blockClicked]);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
if (focusedChild == null) {
|
||||
return;
|
||||
}
|
||||
setBlockFocus(blockId);
|
||||
}, [focusedChild, blockId]);
|
||||
nodeModel.focusNode();
|
||||
}, [focusedChild]);
|
||||
|
||||
// treat the block as clicked on creation
|
||||
const setBlockClickedTrue = React.useCallback(() => {
|
||||
setBlockClicked(true);
|
||||
}, []);
|
||||
|
||||
let viewElem = React.useMemo(
|
||||
() => getViewElem(blockId, blockData?.meta?.view, viewModel),
|
||||
[blockId, blockData?.meta?.view, viewModel]
|
||||
const viewElem = React.useMemo(
|
||||
() => getViewElem(nodeModel.blockId, blockData?.meta?.view, viewModel),
|
||||
[nodeModel.blockId, blockData?.meta?.view, viewModel]
|
||||
);
|
||||
|
||||
const determineFocusedChild = React.useCallback(
|
||||
@@ -193,20 +179,29 @@ const BlockFull = React.memo(({ blockId, layoutModel, viewModel }: FullBlockProp
|
||||
|
||||
return (
|
||||
<BlockFrame
|
||||
key={blockId}
|
||||
blockId={blockId}
|
||||
layoutModel={layoutModel}
|
||||
key={nodeModel.blockId}
|
||||
nodeModel={nodeModel}
|
||||
preview={false}
|
||||
blockModel={blockModel}
|
||||
viewModel={viewModel}
|
||||
>
|
||||
<div key="focuselem" className="block-focuselem">
|
||||
<input type="text" value="" ref={focusElemRef} id={`${blockId}-dummy-focus`} onChange={() => {}} />
|
||||
<input
|
||||
type="text"
|
||||
value=""
|
||||
ref={focusElemRef}
|
||||
id={`${nodeModel.blockId}-dummy-focus`}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
key="content"
|
||||
className="block-content"
|
||||
style={{ pointerEvents: layoutModel?.disablePointerEvents ? "none" : undefined }}
|
||||
style={{
|
||||
pointerEvents: disablePointerEvents ? "none" : undefined,
|
||||
width: addlProps?.transform?.width,
|
||||
height: addlProps?.transform?.height,
|
||||
}}
|
||||
>
|
||||
<ErrorBoundary>
|
||||
<React.Suspense fallback={<CenteredDiv>Loading...</CenteredDiv>}>{viewElem}</React.Suspense>
|
||||
@@ -218,19 +213,19 @@ const BlockFull = React.memo(({ blockId, layoutModel, viewModel }: FullBlockProp
|
||||
|
||||
const Block = React.memo((props: BlockProps) => {
|
||||
counterInc("render-Block");
|
||||
counterInc("render-Block-" + props.blockId.substring(0, 8));
|
||||
const [blockData, loading] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", props.blockId));
|
||||
let viewModel = getViewModel(props.blockId);
|
||||
counterInc("render-Block-" + props.nodeModel.blockId.substring(0, 8));
|
||||
const [blockData, loading] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", props.nodeModel.blockId));
|
||||
let viewModel = getViewModel(props.nodeModel.blockId);
|
||||
if (viewModel == null || viewModel.viewType != blockData?.meta?.view) {
|
||||
viewModel = makeViewModel(props.blockId, blockData?.meta?.view);
|
||||
registerViewModel(props.blockId, viewModel);
|
||||
viewModel = makeViewModel(props.nodeModel.blockId, blockData?.meta?.view);
|
||||
registerViewModel(props.nodeModel.blockId, viewModel);
|
||||
}
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
unregisterViewModel(props.blockId);
|
||||
unregisterViewModel(props.nodeModel.blockId);
|
||||
};
|
||||
}, []);
|
||||
if (loading || util.isBlank(props.blockId) || blockData == null) {
|
||||
if (loading || util.isBlank(props.nodeModel.blockId) || blockData == null) {
|
||||
return null;
|
||||
}
|
||||
if (props.preview) {
|
||||
|
||||
@@ -11,21 +11,22 @@ import {
|
||||
} from "@/app/block/blockutil";
|
||||
import { Button } from "@/app/element/button";
|
||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||
import { atoms, globalStore, useBlockAtom, WOS } from "@/app/store/global";
|
||||
import { atoms, globalStore, WOS } from "@/app/store/global";
|
||||
import * as services from "@/app/store/services";
|
||||
import { MagnifyIcon } from "@/element/magnify";
|
||||
import { useLayoutModel } from "@/layout/index";
|
||||
import { NodeModel } from "@/layout/index";
|
||||
import { checkKeyPressed, keydownWrapper } from "@/util/keyutil";
|
||||
import * as util from "@/util/util";
|
||||
import clsx from "clsx";
|
||||
import * as jotai from "jotai";
|
||||
import * as React from "react";
|
||||
import { BlockFrameProps, LayoutComponentModel } from "./blocktypes";
|
||||
import { BlockFrameProps } from "./blocktypes";
|
||||
|
||||
function handleHeaderContextMenu(
|
||||
e: React.MouseEvent<HTMLDivElement>,
|
||||
blockData: Block,
|
||||
viewModel: ViewModel,
|
||||
magnified: boolean,
|
||||
onMagnifyToggle: () => void,
|
||||
onClose: () => void
|
||||
) {
|
||||
@@ -33,7 +34,7 @@ function handleHeaderContextMenu(
|
||||
e.stopPropagation();
|
||||
let menu: ContextMenuItem[] = [
|
||||
{
|
||||
label: "Magnify Block",
|
||||
label: magnified ? "Un-magnify Block" : "Magnify Block",
|
||||
click: () => {
|
||||
onMagnifyToggle();
|
||||
},
|
||||
@@ -78,20 +79,27 @@ function getViewIconElem(viewIconUnion: string | HeaderIconButton, blockData: Bl
|
||||
}
|
||||
}
|
||||
|
||||
const OptMagnifyButton = React.memo(({ layoutCompModel }: { layoutCompModel: LayoutComponentModel }) => {
|
||||
const magnifyDecl: HeaderIconButton = {
|
||||
elemtype: "iconbutton",
|
||||
icon: <MagnifyIcon enabled={layoutCompModel?.isMagnified} />,
|
||||
title: layoutCompModel?.isMagnified ? "Minimize" : "Magnify",
|
||||
click: layoutCompModel?.onMagnifyToggle,
|
||||
};
|
||||
return <IconButton key="magnify" decl={magnifyDecl} className="block-frame-magnify" />;
|
||||
});
|
||||
const OptMagnifyButton = React.memo(
|
||||
({ magnified, toggleMagnify }: { magnified: boolean; toggleMagnify: () => void }) => {
|
||||
const magnifyDecl: HeaderIconButton = {
|
||||
elemtype: "iconbutton",
|
||||
icon: <MagnifyIcon enabled={magnified} />,
|
||||
title: magnified ? "Minimize" : "Magnify",
|
||||
click: toggleMagnify,
|
||||
};
|
||||
return <IconButton key="magnify" decl={magnifyDecl} className="block-frame-magnify" />;
|
||||
}
|
||||
);
|
||||
|
||||
function computeEndIcons(blockData: Block, viewModel: ViewModel, layoutModel: LayoutComponentModel): JSX.Element[] {
|
||||
function computeEndIcons(
|
||||
viewModel: ViewModel,
|
||||
magnified: boolean,
|
||||
toggleMagnify: () => void,
|
||||
onClose: () => void,
|
||||
onContextMenu: (e: React.MouseEvent<HTMLDivElement>) => void
|
||||
): JSX.Element[] {
|
||||
const endIconsElem: JSX.Element[] = [];
|
||||
const endIconButtons = util.useAtomValueSafe(viewModel.endIconButtons);
|
||||
|
||||
if (endIconButtons && endIconButtons.length > 0) {
|
||||
endIconsElem.push(...endIconButtons.map((button, idx) => <IconButton key={idx} decl={button} />));
|
||||
}
|
||||
@@ -99,30 +107,44 @@ function computeEndIcons(blockData: Block, viewModel: ViewModel, layoutModel: La
|
||||
elemtype: "iconbutton",
|
||||
icon: "cog",
|
||||
title: "Settings",
|
||||
click: (e) =>
|
||||
handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onMagnifyToggle, layoutModel?.onClose),
|
||||
click: onContextMenu,
|
||||
};
|
||||
endIconsElem.push(<IconButton key="settings" decl={settingsDecl} className="block-frame-settings" />);
|
||||
endIconsElem.push(<OptMagnifyButton key="unmagnify" layoutCompModel={layoutModel} />);
|
||||
endIconsElem.push(<OptMagnifyButton key="unmagnify" magnified={magnified} toggleMagnify={toggleMagnify} />);
|
||||
const closeDecl: HeaderIconButton = {
|
||||
elemtype: "iconbutton",
|
||||
icon: "xmark-large",
|
||||
title: "Close",
|
||||
click: layoutModel?.onClose,
|
||||
click: onClose,
|
||||
};
|
||||
endIconsElem.push(<IconButton key="close" decl={closeDecl} className="block-frame-default-close" />);
|
||||
return endIconsElem;
|
||||
}
|
||||
|
||||
const BlockFrame_Header = ({ blockId, layoutModel, viewModel }: BlockFrameProps) => {
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||
const BlockFrame_Header = ({ nodeModel, viewModel, preview }: BlockFrameProps) => {
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", nodeModel.blockId));
|
||||
const viewName = util.useAtomValueSafe(viewModel.viewName) ?? blockViewToName(blockData?.meta?.view);
|
||||
const settingsConfig = jotai.useAtomValue(atoms.settingsConfigAtom);
|
||||
const viewIconUnion = util.useAtomValueSafe(viewModel.viewIcon) ?? blockViewToIcon(blockData?.meta?.view);
|
||||
const preIconButton = util.useAtomValueSafe(viewModel.preIconButton);
|
||||
const headerTextUnion = util.useAtomValueSafe(viewModel.viewText);
|
||||
const magnified = jotai.useAtomValue(nodeModel.isMagnified);
|
||||
const dragHandleRef = preview ? null : nodeModel.dragHandleRef;
|
||||
|
||||
const endIconsElem = computeEndIcons(blockData, viewModel, layoutModel);
|
||||
const onContextMenu = React.useCallback(
|
||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
handleHeaderContextMenu(e, blockData, viewModel, magnified, nodeModel.toggleMagnify, nodeModel.onClose);
|
||||
},
|
||||
[magnified]
|
||||
);
|
||||
|
||||
const endIconsElem = computeEndIcons(
|
||||
viewModel,
|
||||
magnified,
|
||||
nodeModel.toggleMagnify,
|
||||
nodeModel.onClose,
|
||||
onContextMenu
|
||||
);
|
||||
const viewIconElem = getViewIconElem(viewIconUnion, blockData);
|
||||
let preIconButtonElem: JSX.Element = null;
|
||||
if (preIconButton) {
|
||||
@@ -139,23 +161,17 @@ const BlockFrame_Header = ({ blockId, layoutModel, viewModel }: BlockFrameProps)
|
||||
);
|
||||
}
|
||||
} else if (Array.isArray(headerTextUnion)) {
|
||||
headerTextElems.push(...renderHeaderElements(headerTextUnion));
|
||||
headerTextElems.push(...renderHeaderElements(headerTextUnion, preview));
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="block-frame-default-header"
|
||||
ref={layoutModel?.dragHandleRef}
|
||||
onContextMenu={(e) =>
|
||||
handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onMagnifyToggle, layoutModel?.onClose)
|
||||
}
|
||||
>
|
||||
<div className="block-frame-default-header" ref={dragHandleRef} onContextMenu={onContextMenu}>
|
||||
{preIconButtonElem}
|
||||
<div className="block-frame-default-header-iconview">
|
||||
{viewIconElem}
|
||||
<div className="block-frame-view-type">{viewName}</div>
|
||||
{settingsConfig?.blockheader?.showblockids && (
|
||||
<div className="block-frame-blockid">[{blockId.substring(0, 8)}]</div>
|
||||
<div className="block-frame-blockid">[{nodeModel.blockId.substring(0, 8)}]</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="block-frame-textelems-wrapper">{headerTextElems}</div>
|
||||
@@ -164,11 +180,11 @@ const BlockFrame_Header = ({ blockId, layoutModel, viewModel }: BlockFrameProps)
|
||||
);
|
||||
};
|
||||
|
||||
const HeaderTextElem = React.memo(({ elem }: { elem: HeaderElem }) => {
|
||||
const HeaderTextElem = React.memo(({ elem, preview }: { elem: HeaderElem; preview: boolean }) => {
|
||||
if (elem.elemtype == "iconbutton") {
|
||||
return <IconButton decl={elem} className={clsx("block-frame-header-iconbutton", elem.className)} />;
|
||||
} else if (elem.elemtype == "input") {
|
||||
return <Input decl={elem} className={clsx("block-frame-input", elem.className)} />;
|
||||
return <Input decl={elem} className={clsx("block-frame-input", elem.className)} preview={preview} />;
|
||||
} else if (elem.elemtype == "text") {
|
||||
return <div className="block-frame-text">{elem.text}</div>;
|
||||
} else if (elem.elemtype == "textbutton") {
|
||||
@@ -187,7 +203,7 @@ const HeaderTextElem = React.memo(({ elem }: { elem: HeaderElem }) => {
|
||||
onMouseOut={elem.onMouseOut}
|
||||
>
|
||||
{elem.children.map((child, childIdx) => (
|
||||
<HeaderTextElem elem={child} key={childIdx} />
|
||||
<HeaderTextElem elem={child} key={childIdx} preview={preview} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
@@ -195,11 +211,11 @@ const HeaderTextElem = React.memo(({ elem }: { elem: HeaderElem }) => {
|
||||
return null;
|
||||
});
|
||||
|
||||
function renderHeaderElements(headerTextUnion: HeaderElem[]): JSX.Element[] {
|
||||
function renderHeaderElements(headerTextUnion: HeaderElem[], preview: boolean): JSX.Element[] {
|
||||
const headerTextElems: JSX.Element[] = [];
|
||||
for (let idx = 0; idx < headerTextUnion.length; idx++) {
|
||||
const elem = headerTextUnion[idx];
|
||||
const renderedElement = <HeaderTextElem elem={elem} key={idx} />;
|
||||
const renderedElement = <HeaderTextElem elem={elem} key={idx} preview={preview} />;
|
||||
if (renderedElement) {
|
||||
headerTextElems.push(renderedElement);
|
||||
}
|
||||
@@ -207,18 +223,11 @@ function renderHeaderElements(headerTextUnion: HeaderElem[]): JSX.Element[] {
|
||||
return headerTextElems;
|
||||
}
|
||||
|
||||
function BlockNum({ blockId }: { blockId: string }) {
|
||||
const tabId = jotai.useAtomValue(atoms.activeTabId);
|
||||
const tabAtom = WOS.getWaveObjectAtom<Tab>(WOS.makeORef("tab", tabId));
|
||||
const layoutModel = useLayoutModel(tabAtom);
|
||||
const leafsOrdered = jotai.useAtomValue(layoutModel.leafsOrdered);
|
||||
const index = React.useMemo(() => leafsOrdered.findIndex((leaf) => leaf.data?.blockId == blockId), [leafsOrdered]);
|
||||
return index !== -1 ? index + 1 : null;
|
||||
}
|
||||
|
||||
const BlockMask = ({ blockId, preview, isFocused }: { blockId: string; preview: boolean; isFocused: boolean }) => {
|
||||
const BlockMask = ({ nodeModel }: { nodeModel: NodeModel }) => {
|
||||
const isFocused = jotai.useAtomValue(nodeModel.isFocused);
|
||||
const blockNum = jotai.useAtomValue(nodeModel.blockNum);
|
||||
const isLayoutMode = jotai.useAtomValue(atoms.controlShiftDelayAtom);
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", nodeModel.blockId));
|
||||
|
||||
const style: React.CSSProperties = {};
|
||||
if (!isFocused && blockData?.meta?.["frame:bordercolor"]) {
|
||||
@@ -231,9 +240,7 @@ const BlockMask = ({ blockId, preview, isFocused }: { blockId: string; preview:
|
||||
if (isLayoutMode) {
|
||||
innerElem = (
|
||||
<div className="block-mask-inner">
|
||||
<div className="bignum">
|
||||
<BlockNum blockId={blockId} />
|
||||
</div>
|
||||
<div className="bignum">{blockNum}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -245,27 +252,17 @@ const BlockMask = ({ blockId, preview, isFocused }: { blockId: string; preview:
|
||||
};
|
||||
|
||||
const BlockFrame_Default_Component = (props: BlockFrameProps) => {
|
||||
const { blockId, layoutModel, viewModel, blockModel, preview, numBlocksInTab, children } = props;
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||
const isFocusedAtom = useBlockAtom<boolean>(blockId, "isFocused", () => {
|
||||
return jotai.atom((get) => {
|
||||
const winData = get(atoms.waveWindow);
|
||||
return winData?.activeblockid === blockId;
|
||||
});
|
||||
});
|
||||
const { nodeModel, viewModel, blockModel, preview, numBlocksInTab, children } = props;
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", nodeModel.blockId));
|
||||
const isFocused = jotai.useAtomValue(nodeModel.isFocused);
|
||||
const viewIconUnion = util.useAtomValueSafe(viewModel.viewIcon) ?? blockViewToIcon(blockData?.meta?.view);
|
||||
const customBg = util.useAtomValueSafe(viewModel.blockBg);
|
||||
|
||||
let isFocused = jotai.useAtomValue(isFocusedAtom);
|
||||
if (preview) {
|
||||
isFocused = true;
|
||||
}
|
||||
|
||||
const viewIconElem = getViewIconElem(viewIconUnion, blockData);
|
||||
|
||||
function handleKeyDown(waveEvent: WaveKeyboardEvent): boolean {
|
||||
if (checkKeyPressed(waveEvent, "Cmd:m")) {
|
||||
layoutModel?.onMagnifyToggle();
|
||||
nodeModel.toggleMagnify();
|
||||
return true;
|
||||
}
|
||||
if (viewModel?.keyDownHandler) {
|
||||
@@ -286,20 +283,17 @@ const BlockFrame_Default_Component = (props: BlockFrameProps) => {
|
||||
const previewElem = <div className="block-frame-preview">{viewIconElem}</div>;
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"block",
|
||||
"block-frame-default",
|
||||
isFocused ? "block-focused" : null,
|
||||
preview ? "block-preview" : null,
|
||||
numBlocksInTab == 1 ? "block-no-highlight" : null,
|
||||
"block-" + blockId
|
||||
)}
|
||||
className={clsx("block", "block-frame-default", "block-" + nodeModel.blockId, {
|
||||
"block-focused": isFocused || preview,
|
||||
"block-preview": preview,
|
||||
"block-no-highlight": numBlocksInTab === 1,
|
||||
})}
|
||||
onClick={blockModel?.onClick}
|
||||
onFocusCapture={blockModel?.onFocusCapture}
|
||||
ref={blockModel?.blockRef}
|
||||
onKeyDown={keydownWrapper(handleKeyDown)}
|
||||
>
|
||||
<BlockMask blockId={blockId} preview={preview} isFocused={isFocused} />
|
||||
<BlockMask nodeModel={nodeModel} />
|
||||
<div className="block-frame-default-inner" style={innerStyle}>
|
||||
<BlockFrame_Header {...props} />
|
||||
{preview ? previewElem : children}
|
||||
@@ -311,7 +305,7 @@ const BlockFrame_Default_Component = (props: BlockFrameProps) => {
|
||||
const BlockFrame_Default = React.memo(BlockFrame_Default_Component) as typeof BlockFrame_Default_Component;
|
||||
|
||||
const BlockFrame = React.memo((props: BlockFrameProps) => {
|
||||
const blockId = props.blockId;
|
||||
const blockId = props.nodeModel.blockId;
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||
const tabData = jotai.useAtomValue(atoms.tabAtom);
|
||||
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export interface LayoutComponentModel {
|
||||
disablePointerEvents: boolean;
|
||||
onClose?: () => void;
|
||||
onMagnifyToggle?: () => void;
|
||||
isMagnified: boolean;
|
||||
dragHandleRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
import { NodeModel } from "@/layout/index";
|
||||
export interface BlockProps {
|
||||
blockId: string;
|
||||
preview: boolean;
|
||||
layoutModel: LayoutComponentModel;
|
||||
nodeModel: NodeModel;
|
||||
}
|
||||
|
||||
export interface BlockComponentModel {
|
||||
@@ -22,9 +14,8 @@ export interface BlockComponentModel {
|
||||
}
|
||||
|
||||
export interface BlockFrameProps {
|
||||
blockId: string;
|
||||
blockModel?: BlockComponentModel;
|
||||
layoutModel?: LayoutComponentModel;
|
||||
nodeModel?: NodeModel;
|
||||
viewModel?: ViewModel;
|
||||
preview: boolean;
|
||||
numBlocksInTab?: number;
|
||||
|
||||
@@ -196,20 +196,26 @@ export const ConnectionButton = React.memo(({ decl }: { decl: ConnectionButton }
|
||||
);
|
||||
});
|
||||
|
||||
export const Input = React.memo(({ decl, className }: { decl: HeaderInput; className: string }) => {
|
||||
const { value, ref, isDisabled, onChange, onKeyDown, onFocus, onBlur } = decl;
|
||||
return (
|
||||
<div className="input-wrapper">
|
||||
<input
|
||||
ref={ref}
|
||||
disabled={isDisabled}
|
||||
className={className}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e)}
|
||||
onKeyDown={(e) => onKeyDown(e)}
|
||||
onFocus={(e) => onFocus(e)}
|
||||
onBlur={(e) => onBlur(e)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export const Input = React.memo(
|
||||
({ decl, className, preview }: { decl: HeaderInput; className: string; preview: boolean }) => {
|
||||
const { value, ref, isDisabled, onChange, onKeyDown, onFocus, onBlur } = decl;
|
||||
return (
|
||||
<div className="input-wrapper">
|
||||
<input
|
||||
ref={
|
||||
!preview
|
||||
? ref
|
||||
: undefined /* don't wire up the input field if the preview block is being rendered */
|
||||
}
|
||||
disabled={isDisabled}
|
||||
className={className}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e)}
|
||||
onKeyDown={(e) => onKeyDown(e)}
|
||||
onFocus={(e) => onFocus(e)}
|
||||
onBlur={(e) => onBlur(e)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user