chore: import upstream snapshot with attribution
CI / build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:09:51 +08:00
commit 0232b4e2bb
3528 changed files with 291404 additions and 0 deletions
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
/* eslint-disable react/prop-types */
import { useEffect, type CSSProperties } from 'react';
import React from 'react';
import type { Rectangle } from '@flowgram.ai/utils';
import { FlowGroupController } from '@flowgram.ai/document';
import { IGroupBox } from '../type';
import { useHover } from './hooks';
export const GroupBox: IGroupBox = (props) => {
const { groupNode } = props;
const groupController = FlowGroupController.create(groupNode)!;
const bounds: Rectangle = groupController.bounds;
const { hover, ref } = useHover();
const positionStyle: CSSProperties = {
position: 'absolute',
left: bounds.left,
top: bounds.top,
width: bounds.width,
height: bounds.height,
};
const defaultBackgroundStyle: CSSProperties = {
borderRadius: 10,
zIndex: -1,
outline: `${hover ? 2 : 1}px solid rgb(97, 69, 211)`,
backgroundColor: 'rgb(236 233 247)',
};
const backgroundStyle = props.backgroundStyle
? props.backgroundStyle(groupController)
: defaultBackgroundStyle;
useEffect(() => {
groupController.hovered = hover;
}, [hover]);
if (!groupController || groupController.collapsed) {
return <></>;
}
return (
<div className="gedit-group-box" data-group-id={groupNode.id}>
<div
className="gedit-group-background"
ref={ref}
style={{
...positionStyle,
...backgroundStyle,
}}
/>
</div>
);
};
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
/* eslint-disable react/prop-types */
import React, { useCallback, useEffect, useState } from 'react';
import { FlowGroupController, FlowNodeEntity, FlowNodeRenderData } from '@flowgram.ai/document';
import { FlowDocument } from '@flowgram.ai/document';
import { useEntityFromContext, useService } from '@flowgram.ai/core';
import { delay, Rectangle } from '@flowgram.ai/utils';
import { IGroupRender } from '../type';
function useCurrentDomNode(): HTMLDivElement {
const entity = useEntityFromContext<FlowNodeEntity>();
const renderData = entity.getData<FlowNodeRenderData>(FlowNodeRenderData);
return renderData.node;
}
export const GroupRender: IGroupRender = props => {
const { groupNode, GroupNode, GroupBoxHeader } = props;
const container = useCurrentDomNode();
const document = useService<FlowDocument>(FlowDocument);
const groupController = FlowGroupController.create(groupNode);
const [key, setKey] = useState(0);
const [rendering, setRendering] = useState(true);
const [collapsedCache, setCollapsedCache] = useState(groupController?.collapsed ?? false);
const rerender = useCallback(async () => {
setRendering(true);
setKey(key + 1);
// 边框bounds计算会有延迟
await delay(50);
setKey(key + 1);
setRendering(false);
}, [key]);
// 监听 collapsed 变化触发重渲染
useEffect(() => {
const disposer = document.renderTree.onTreeChange(() => {
if (groupController?.collapsed !== collapsedCache) {
setCollapsedCache(groupController?.collapsed ?? false);
rerender();
}
});
return () => {
disposer.dispose();
};
}, [key]);
// 首次渲染时如果分组是展开状态,此时边框bounds计算会有延迟,需要强制重新渲染
useEffect(() => {
if (!groupController || groupController.collapsed) {
return;
}
rerender();
}, []);
if (!groupController) {
return <></>;
}
const groupNodeRender = (
<GroupNode key={key} groupNode={groupNode} groupController={groupController} />
);
const groupBoxHeader = (
<GroupBoxHeader key={key} groupController={groupController} groupNode={groupNode} />
);
if (groupController.collapsed) {
const positionStyle: Partial<CSSStyleDeclaration> = {
display: 'block',
zIndex: '0',
width: 'auto',
height: 'auto',
};
Object.assign(container.style, positionStyle);
return groupNodeRender;
} else if (!rendering) {
const bounds: Rectangle = groupController.bounds;
const positionStyle: Partial<CSSStyleDeclaration> = {
width: `${bounds.width}px`,
};
Object.assign(container.style, positionStyle);
return groupBoxHeader;
} else {
return <></>;
}
};
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
import { useEffect, useRef, useState } from 'react';
export const useHover = () => {
const ref = useRef<HTMLDivElement>(null);
const [hover, setHover] = useState(false);
const checkMouseOver = (event: MouseEvent) => {
if (!ref.current) {
return;
}
const { left, top, right, bottom } = ref.current.getBoundingClientRect();
const isOver =
event.clientX >= left &&
event.clientX <= right &&
event.clientY >= top &&
event.clientY <= bottom;
setHover(isOver);
};
useEffect(() => {
window.addEventListener('mousemove', checkMouseOver);
return () => {
window.removeEventListener('mousemove', checkMouseOver);
};
}, []);
return {
hover,
ref,
};
};
@@ -0,0 +1,7 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
export { GroupRender } from './group-render';
export { GroupBox } from './group-box';