F: Implementing block UI pt2

This commit is contained in:
Ivan 2025-04-22 00:41:13 +03:00
parent a69a26bb7b
commit e71a5baf60
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 { Footer } from './footer';
import { GlobalDialogs } from './global-dialogs';
import { GlobalLoader } from './global-loader1';
import { GlobalLoader } from './global-loader';
import { ToasterThemed } from './global-toaster';
import { GlobalTooltips } from './global-tooltips';
import { MutationErrors } from './mutation-errors';

View File

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

View File

@ -16,8 +16,9 @@ export const BLOCK_NODE_MIN_HEIGHT = 100;
export function BlockNode(node: BlockInternalNode) {
const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
const { selected, schema } = useOssEdit();
const singleSelected = selected.length === 1 ? selected[0] : null;
const isParent = !singleSelected ? false : schema.hierarchy.at(singleSelected)?.inputs.includes(node.data.block.id);
const focus = selected.length === 1 ? selected[0] : null;
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 (
<>
<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)}`}
</div>
) : 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
className={clsx(
'w-fit mx-auto -translate-y-[14px]',

View File

@ -8,7 +8,7 @@ export function InputNode(node: OperationInternalNode) {
return (
<>
<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 { IconConsolidation, IconRSForm } from '@/components/icons';
import { cn } from '@/components/utils';
import { Indicator } from '@/components/view';
import { globalIDs } from '@/utils/constants';
import { OperationType } from '../../../../backend/types';
import { type OperationInternalNode } from '../../../../models/oss-layout';
import { useOperationTooltipStore } from '../../../../stores/operation-tooltip';
import { useOssEdit } from '../../oss-edit-context';
export const OPERATION_NODE_WIDTH = 150;
export const OPERATION_NODE_HEIGHT = 40;
@ -23,6 +25,10 @@ interface 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 showCoordinates = useOSSGraphStore(state => state.showCoordinates);
@ -31,7 +37,11 @@ export function NodeCore({ node }: NodeCoreProps) {
return (
<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-hidden={node.dragging}
onMouseEnter={() => setHover(node.data.operation)}
@ -55,7 +65,8 @@ export function NodeCore({ node }: NodeCoreProps) {
className={clsx(
'absolute top-full mt-1 right-[1px]',
'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)}`}
@ -63,7 +74,7 @@ export function NodeCore({ node }: NodeCoreProps) {
) : null}
{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}
{!node.data.operation.is_owned ? (

View File

@ -9,10 +9,10 @@ import { NodeCore } from './node-core';
export function OperationNode(node: OperationInternalNode) {
return (
<>
<Handle type='target' position={Position.Top} id='left' style={{ left: 40 }} />
<Handle type='target' position={Position.Top} id='right' style={{ right: 40, left: 'auto' }} />
<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', top: -2 }} />
<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 {
background-color: transparent;
border-color: transparent;
z-index: var(--z-index-navigation);
color: var(--color-muted-foreground);
width: 0;
height: 0;
&:hover {
color: var(--color-foreground);
}
width: 0;
height: 0;
}
.react-flow__node-input,
@ -21,28 +22,15 @@
cursor: pointer;
border-radius: 5px;
padding: 2px;
width: 150px;
height: fit-content;
border-width: 0;
outline-offset: -2px;
outline-style: solid;
outline-color: transparent;
padding: 0;
margin: 0;
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);
}
background-color: transparent;
}
.react-flow__node-block {
cursor: auto;
border-radius: 5px;
border-width: 0;
@ -53,7 +41,34 @@
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 {
cursor: default;
border-radius: 5px;
border-style: dashed;
border-width: 2px;
@ -61,6 +76,7 @@
padding: 4px;
color: var(--color-muted-foreground);
background-color: transparent;
.selected & {
color: var(--color-foreground);

View File

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

View File

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

View File

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

View File

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

View File

@ -6,6 +6,7 @@
/* stylelint-disable selector-class-pattern */
.react-flow__handle {
z-index: var(--z-index-navigation);
cursor: default !important;
border-radius: 9999px;
@ -36,13 +37,6 @@
}
[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) {
box-shadow: 0 0 0 2px var(--color-selected) !important;
}