F: Implementing block UI pt2

This commit is contained in:
Ivan 2025-04-22 00:39:18 +03:00
parent 13914a04f9
commit a78a594509
13 changed files with 97 additions and 52 deletions

View File

@ -9,7 +9,7 @@ import { useDialogsStore } from '@/stores/dialogs';
import { NavigationState } from './navigation/navigation-context'; import { NavigationState } from './navigation/navigation-context';
import { Footer } from './footer'; import { Footer } from './footer';
import { GlobalDialogs } from './global-dialogs'; import { GlobalDialogs } from './global-dialogs';
import { GlobalLoader } from './global-loader1'; import { GlobalLoader } from './global-loader';
import { ToasterThemed } from './global-toaster'; import { ToasterThemed } from './global-toaster';
import { GlobalTooltips } from './global-tooltips'; import { GlobalTooltips } from './global-tooltips';
import { MutationErrors } from './mutation-errors'; import { MutationErrors } from './mutation-errors';

View File

@ -543,26 +543,26 @@ export function calculateNewBlockPosition(data: ICreateBlockDTO, layout: IOssLay
let right = undefined; let right = undefined;
let bottom = undefined; let bottom = undefined;
for (const node of block_nodes) { for (const block of block_nodes) {
left = !left ? node.x - GRID_SIZE : Math.min(left, node.x - GRID_SIZE); left = !left ? block.x - GRID_SIZE : Math.min(left, block.x - GRID_SIZE);
top = !top ? node.y - GRID_SIZE : Math.min(top, node.y - GRID_SIZE); top = !top ? block.y - GRID_SIZE : Math.min(top, block.y - GRID_SIZE);
right = !right right = !right
? Math.max(left + data.width, node.x + node.width + GRID_SIZE) ? Math.max(left + data.width, block.x + block.width + GRID_SIZE)
: Math.max(right, node.x + node.width + GRID_SIZE); : Math.max(right, block.x + block.width + GRID_SIZE);
bottom = !bottom bottom = !bottom
? Math.max(top + data.height, node.y + node.height + GRID_SIZE) ? Math.max(top + data.height, block.y + block.height + GRID_SIZE)
: Math.max(bottom, node.y + node.height + GRID_SIZE); : Math.max(bottom, block.y + block.height + GRID_SIZE);
} }
for (const node of operation_nodes) { for (const operation of operation_nodes) {
left = !left ? node.x - GRID_SIZE : Math.min(left, node.x - GRID_SIZE); left = !left ? operation.x - GRID_SIZE : Math.min(left, operation.x - GRID_SIZE);
top = !top ? node.y - GRID_SIZE : Math.min(top, node.y - GRID_SIZE); top = !top ? operation.y - GRID_SIZE : Math.min(top, operation.y - GRID_SIZE);
right = !right right = !right
? Math.max(left + data.width, node.x + OPERATION_NODE_WIDTH + GRID_SIZE) ? Math.max(left + data.width, operation.x + OPERATION_NODE_WIDTH + GRID_SIZE)
: Math.max(right, node.x + OPERATION_NODE_WIDTH + GRID_SIZE); : Math.max(right, operation.x + OPERATION_NODE_WIDTH + GRID_SIZE);
bottom = !bottom bottom = !bottom
? Math.max(top + data.height, node.y + OPERATION_NODE_HEIGHT + GRID_SIZE) ? Math.max(top + data.height, operation.y + OPERATION_NODE_HEIGHT + GRID_SIZE)
: Math.max(bottom, node.y + OPERATION_NODE_HEIGHT + GRID_SIZE); : Math.max(bottom, operation.y + OPERATION_NODE_HEIGHT + GRID_SIZE);
} }
return { return {

View File

@ -16,8 +16,9 @@ export const BLOCK_NODE_MIN_HEIGHT = 100;
export function BlockNode(node: BlockInternalNode) { export function BlockNode(node: BlockInternalNode) {
const showCoordinates = useOSSGraphStore(state => state.showCoordinates); const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
const { selected, schema } = useOssEdit(); const { selected, schema } = useOssEdit();
const singleSelected = selected.length === 1 ? selected[0] : null; const focus = selected.length === 1 ? selected[0] : null;
const isParent = !singleSelected ? false : schema.hierarchy.at(singleSelected)?.inputs.includes(node.data.block.id); const isParent = (!!focus && schema.hierarchy.at(focus)?.inputs.includes(-node.data.block.id)) ?? false;
const isChild = (!!focus && schema.hierarchy.at(focus)?.outputs.includes(-node.data.block.id)) ?? false;
return ( return (
<> <>
<NodeResizeControl minWidth={BLOCK_NODE_MIN_WIDTH} minHeight={BLOCK_NODE_MIN_HEIGHT}> <NodeResizeControl minWidth={BLOCK_NODE_MIN_WIDTH} minHeight={BLOCK_NODE_MIN_HEIGHT}>
@ -34,7 +35,13 @@ export function BlockNode(node: BlockInternalNode) {
{`X: ${node.xPos.toFixed(0)} Y: ${node.yPos.toFixed(0)}`} {`X: ${node.xPos.toFixed(0)} Y: ${node.yPos.toFixed(0)}`}
</div> </div>
) : null} ) : null}
<div className={clsx('cc-node-block h-full w-full', isParent && 'border-primary')}> <div
className={clsx(
'cc-node-block h-full w-full',
isParent && 'border-primary',
isChild && 'border-accent-orange50'
)}
>
<div <div
className={clsx( className={clsx(
'w-fit mx-auto -translate-y-[14px]', 'w-fit mx-auto -translate-y-[14px]',

View File

@ -8,7 +8,7 @@ export function InputNode(node: OperationInternalNode) {
return ( return (
<> <>
<NodeCore node={node} /> <NodeCore node={node} />
<Handle type='source' position={Position.Bottom} /> <Handle type='source' position={Position.Bottom} className='-translate-y-[1px]' />
</> </>
); );
} }

View File

@ -5,12 +5,14 @@ import clsx from 'clsx';
import { useOSSGraphStore } from '@/features/oss/stores/oss-graph'; import { useOSSGraphStore } from '@/features/oss/stores/oss-graph';
import { IconConsolidation, IconRSForm } from '@/components/icons'; import { IconConsolidation, IconRSForm } from '@/components/icons';
import { cn } from '@/components/utils';
import { Indicator } from '@/components/view'; import { Indicator } from '@/components/view';
import { globalIDs } from '@/utils/constants'; import { globalIDs } from '@/utils/constants';
import { OperationType } from '../../../../backend/types'; import { OperationType } from '../../../../backend/types';
import { type OperationInternalNode } from '../../../../models/oss-layout'; import { type OperationInternalNode } from '../../../../models/oss-layout';
import { useOperationTooltipStore } from '../../../../stores/operation-tooltip'; import { useOperationTooltipStore } from '../../../../stores/operation-tooltip';
import { useOssEdit } from '../../oss-edit-context';
export const OPERATION_NODE_WIDTH = 150; export const OPERATION_NODE_WIDTH = 150;
export const OPERATION_NODE_HEIGHT = 40; export const OPERATION_NODE_HEIGHT = 40;
@ -23,6 +25,10 @@ interface NodeCoreProps {
} }
export function NodeCore({ node }: NodeCoreProps) { export function NodeCore({ node }: NodeCoreProps) {
const { selected, schema } = useOssEdit();
const focus = selected.length === 1 ? selected[0] : null;
const isChild = (!!focus && schema.hierarchy.at(focus)?.outputs.includes(node.data.operation.id)) ?? false;
const setHover = useOperationTooltipStore(state => state.setActiveOperation); const setHover = useOperationTooltipStore(state => state.setActiveOperation);
const showCoordinates = useOSSGraphStore(state => state.showCoordinates); const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
@ -31,7 +37,11 @@ export function NodeCore({ node }: NodeCoreProps) {
return ( return (
<div <div
className='relative h-[34px] w-[144px] flex items-center justify-center' className={cn(
'cc-node-operation h-[40px] w-[150px]',
'relative flex items-center justify-center p-[2px]',
isChild && 'border-accent-orange50!'
)}
data-tooltip-id={globalIDs.operation_tooltip} data-tooltip-id={globalIDs.operation_tooltip}
data-tooltip-hidden={node.dragging} data-tooltip-hidden={node.dragging}
onMouseEnter={() => setHover(node.data.operation)} onMouseEnter={() => setHover(node.data.operation)}
@ -55,7 +65,8 @@ export function NodeCore({ node }: NodeCoreProps) {
className={clsx( className={clsx(
'absolute top-full mt-1 right-[1px]', 'absolute top-full mt-1 right-[1px]',
'text-[7px]/[8px] font-math', 'text-[7px]/[8px] font-math',
'text-muted-foreground hover:text-foreground' 'text-muted-foreground hover:text-foreground',
node.selected && 'translate-y-[6px]'
)} )}
> >
{`X: ${node.xPos.toFixed(0)} Y: ${node.yPos.toFixed(0)}`} {`X: ${node.xPos.toFixed(0)} Y: ${node.yPos.toFixed(0)}`}
@ -63,7 +74,7 @@ export function NodeCore({ node }: NodeCoreProps) {
) : null} ) : null}
{node.data.operation.operation_type === OperationType.INPUT ? ( {node.data.operation.operation_type === OperationType.INPUT ? (
<div className='absolute top-[1px] right-1/2 translate-x-1/2 border-t w-[30px]' /> <div className='absolute top-[3px] right-1/2 translate-x-1/2 border-t w-[30px]' />
) : null} ) : null}
{!node.data.operation.is_owned ? ( {!node.data.operation.is_owned ? (

View File

@ -9,10 +9,10 @@ import { NodeCore } from './node-core';
export function OperationNode(node: OperationInternalNode) { export function OperationNode(node: OperationInternalNode) {
return ( return (
<> <>
<Handle type='target' position={Position.Top} id='left' style={{ left: 40 }} /> <Handle type='target' position={Position.Top} id='left' style={{ left: 40, top: -2 }} />
<Handle type='target' position={Position.Top} id='right' style={{ right: 40, left: 'auto' }} /> <Handle type='target' position={Position.Top} id='right' style={{ right: 40, left: 'auto', top: -2 }} />
<NodeCore node={node} /> <NodeCore node={node} />
<Handle type='source' position={Position.Bottom} /> <Handle type='source' position={Position.Bottom} className='-translate-y-[1px]' />
</> </>
); );
} }

View File

@ -4,16 +4,17 @@
.react-flow__resize-control.handle { .react-flow__resize-control.handle {
background-color: transparent; background-color: transparent;
border-color: transparent;
z-index: var(--z-index-navigation); z-index: var(--z-index-navigation);
color: var(--color-muted-foreground); color: var(--color-muted-foreground);
width: 0;
height: 0;
&:hover { &:hover {
color: var(--color-foreground); color: var(--color-foreground);
} }
width: 0;
height: 0;
} }
.react-flow__node-input, .react-flow__node-input,
@ -21,28 +22,15 @@
cursor: pointer; cursor: pointer;
border-radius: 5px; border-radius: 5px;
padding: 2px; border-width: 0;
width: 150px;
height: fit-content;
outline-offset: -2px; padding: 0;
outline-style: solid; margin: 0;
outline-color: transparent;
transition-property: outline-offset; background-color: transparent;
transition-timing-function: var(--transition-bezier);
transition-duration: var(--duration-select);
&.selected {
outline-offset: 4px;
outline-color: var(--color-graph-selected);
border-color: var(--color-foreground);
}
} }
.react-flow__node-block { .react-flow__node-block {
cursor: auto;
border-radius: 5px; border-radius: 5px;
border-width: 0; border-width: 0;
@ -53,7 +41,34 @@
pointer-events: none; pointer-events: none;
} }
.cc-node-operation {
cursor: pointer;
border-radius: 5px;
border-color: var(--color-muted-foreground);
border-width: 1px;
color: var(--color-foreground);
background-color: var(--color-card);
outline-offset: -2px;
outline-style: solid;
outline-color: transparent;
transition-property: outline-offset;
transition-timing-function: var(--transition-bezier);
transition-duration: var(--duration-select);
.selected & {
outline-offset: 4px;
outline-color: var(--color-graph-selected);
border-color: var(--color-foreground);
}
}
.cc-node-block { .cc-node-block {
cursor: default;
border-radius: 5px; border-radius: 5px;
border-style: dashed; border-style: dashed;
border-width: 2px; border-width: 2px;
@ -61,6 +76,7 @@
padding: 4px; padding: 4px;
color: var(--color-muted-foreground); color: var(--color-muted-foreground);
background-color: transparent;
.selected & { .selected & {
color: var(--color-foreground); color: var(--color-foreground);

View File

@ -200,6 +200,10 @@ export function OssFlow() {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (node.type === 'block') {
return;
}
setMenuProps({ setMenuProps({
operation: node.data.operation, operation: node.data.operation,
cursorX: event.clientX, cursorX: event.clientX,

View File

@ -3,9 +3,13 @@
/* stylelint-disable selector-class-pattern */ /* stylelint-disable selector-class-pattern */
.react-flow__node-token { .react-flow__node-token {
font-size: 14px;
cursor: default; cursor: default;
border: 1px solid;
border-color: var(--color-border);
border-radius: 100%; border-radius: 100%;
width: 40px; width: 40px;
height: 40px; height: 40px;

View File

@ -3,9 +3,14 @@
/* stylelint-disable selector-class-pattern */ /* stylelint-disable selector-class-pattern */
.react-flow__node-step { .react-flow__node-step {
font-size: 14px;
cursor: default; cursor: default;
color: var(--color-foreground);
border: 1px solid;
border-color: var(--color-border);
border-radius: 100%; border-radius: 100%;
width: 40px; width: 40px;
height: 40px; height: 40px;

View File

@ -4,8 +4,12 @@
.react-flow__node-concept { .react-flow__node-concept {
cursor: default; cursor: default;
font-size: 14px;
border: 1px solid;
border-color: var(--color-border);
border-radius: 100%; border-radius: 100%;
width: 40px; width: 40px;
height: 40px; height: 40px;

View File

@ -6,6 +6,7 @@
/* stylelint-disable selector-class-pattern */ /* stylelint-disable selector-class-pattern */
.react-flow__handle { .react-flow__handle {
z-index: var(--z-index-navigation);
cursor: default !important; cursor: default !important;
border-radius: 9999px; border-radius: 9999px;
@ -36,13 +37,6 @@
} }
[class*='react-flow__node-'] { [class*='react-flow__node-'] {
font-size: 14px;
border: 1px solid;
color: var(--color-foreground);
border-color: var(--color-border);
background-color: var(--color-card);
&:hover:not(.selected) { &:hover:not(.selected) {
box-shadow: 0 0 0 2px var(--color-selected) !important; box-shadow: 0 0 0 2px var(--color-selected) !important;
} }