feat: add customizable port colors to WorkflowPortRender (#360)

* feat(workflow-port): add customizable port colors

* fix(workflow-colors): unify CSS variable naming to --g-workflow-* format - Update LineColors enum and demo configuration - Add CSS variable definitions - Ensure consistent naming across all workflow-related variables

---------

Co-authored-by: husky-dot <xiaozhi@xiaozhideMacBook-Pro.local>
Co-authored-by: husky-dot <xiaozhi@172-0-8-36.lightspeed.rcsntx.sbcglobal.net>
This commit is contained in:
小智 2025-06-10 18:26:30 +08:00 committed by GitHub
parent f994881b22
commit a6d61d347e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 163 additions and 60 deletions

View File

@ -77,12 +77,12 @@ export function useEditorProps(
return json; return json;
}, },
lineColor: { lineColor: {
hidden: 'var(--g-line-color-hidden,transparent)', hidden: 'var(--g-workflow-line-color-hidden,transparent)',
default: 'var(--g-line-color-default,#4d53e8)', default: 'var(--g-workflow-line-color-default,#4d53e8)',
drawing: 'var(--g-line-color-drawing, #5DD6E3)', drawing: 'var(--g-workflow-line-color-drawing, #5DD6E3)',
hovered: 'var(--g-line-color-hover,#37d0ff)', hovered: 'var(--g-workflow-line-color-hover,#37d0ff)',
selected: 'var(--g-line-color-selected,#37d0ff)', selected: 'var(--g-workflow-line-color-selected,#37d0ff)',
error: 'var(--g-line-color-hover,red)', error: 'var(--g-workflow-line-color-error,red)',
}, },
/* /*
* Check whether the line can be added * Check whether the line can be added

View File

@ -1,8 +1,17 @@
:root { :root {
/* Port colors */
--g-workflow-port-color-primary: #4d53e8; --g-workflow-port-color-primary: #4d53e8;
--g-workflow-port-color-secondary: #9197f1; --g-workflow-port-color-secondary: #9197f1;
--g-workflow-port-color-error: #ff0000; --g-workflow-port-color-error: #ff0000;
--g-workflow-port-color-background: #ffffff; --g-workflow-port-color-background: #ffffff;
/* Line colors */
--g-workflow-line-color-hidden: transparent;
--g-workflow-line-color-default: #4d53e8;
--g-workflow-line-color-drawing: #5dd6e3;
--g-workflow-line-color-hover: #37d0ff;
--g-workflow-line-color-selected: #37d0ff;
--g-workflow-line-color-error: red;
} }
.gedit-selector-bounds-background { .gedit-selector-bounds-background {

View File

@ -17,7 +17,15 @@ export const BaseNode = () => {
* https://github.com/bytedance/flowgram.ai/blob/main/packages/client/free-layout-editor/src/components/workflow-node-renderer.tsx * https://github.com/bytedance/flowgram.ai/blob/main/packages/client/free-layout-editor/src/components/workflow-node-renderer.tsx
*/ */
return ( return (
<WorkflowNodeRenderer className="demo-free-node" node={props.node}> <WorkflowNodeRenderer
className="demo-free-node"
node={props.node}
// Optional port color customization
portPrimaryColor="#4d53e8" // Active state color (linked/hovered)
portSecondaryColor="#9197f1" // Default state color
portErrorColor="#ff4444" // Error state color
portBackgroundColor="#ffffff" // Background color
>
{ {
// Form rendering through formMeta generation // Form rendering through formMeta generation
form?.render() form?.render()

View File

@ -56,6 +56,15 @@ function BaseNode() {
Ports are ultimately rendered through the `WorkflowPortRender` component, supporting custom styles, or businesses can reimplement this component based on the source code, see [Free Layout Best Practices - Node Rendering](https://github.com/bytedance/flowgram.ai/blob/main/apps/demo-free-layout/src/components/base-node/node-wrapper.tsx) Ports are ultimately rendered through the `WorkflowPortRender` component, supporting custom styles, or businesses can reimplement this component based on the source code, see [Free Layout Best Practices - Node Rendering](https://github.com/bytedance/flowgram.ai/blob/main/apps/demo-free-layout/src/components/base-node/node-wrapper.tsx)
### Custom Port Colors
You can customize port colors by passing color props to `WorkflowPortRender`:
- `primaryColor` - Active state color (when linked or hovered)
- `secondaryColor` - Default state color
- `errorColor` - Error state color
- `backgroundColor` - Background color
```tsx pure ```tsx pure
import { WorkflowPortRender, useNodeRender } from '@flowgram.ai/free-layout-editor'; import { WorkflowPortRender, useNodeRender } from '@flowgram.ai/free-layout-editor';
@ -67,7 +76,17 @@ function BaseNode() {
<div data-port-id="condition-if-0" data-port-type="output"></div> <div data-port-id="condition-if-0" data-port-type="output"></div>
<div data-port-id="condition-if-1" data-port-type="output"></div> <div data-port-id="condition-if-1" data-port-type="output"></div>
{ports.map((p) => ( {ports.map((p) => (
<WorkflowPortRender key={p.id} entity={p} className="xxx" style={{ /* custom style */}}/> <WorkflowPortRender
key={p.id}
entity={p}
className="xxx"
style={{ /* custom style */}}
// Custom port colors
primaryColor="#4d53e8" // Active state color (linked/hovered)
secondaryColor="#9197f1" // Default state color
errorColor="#ff4444" // Error state color
backgroundColor="#ffffff" // Background color
/>
))} ))}
</div> </div>
) )

View File

@ -17,7 +17,15 @@ export const BaseNode = () => {
* https://github.com/bytedance/flowgram.ai/blob/main/packages/client/free-layout-editor/src/components/workflow-node-renderer.tsx * https://github.com/bytedance/flowgram.ai/blob/main/packages/client/free-layout-editor/src/components/workflow-node-renderer.tsx
*/ */
return ( return (
<WorkflowNodeRenderer className="demo-free-node" node={props.node}> <WorkflowNodeRenderer
className="demo-free-node"
node={props.node}
// 可选的端口颜色自定义
portPrimaryColor="#4d53e8" // 激活状态颜色 (linked/hovered)
portSecondaryColor="#9197f1" // 默认状态颜色
portErrorColor="#ff4444" // 错误状态颜色
portBackgroundColor="#ffffff" // 背景颜色
>
{ {
// 表单渲染通过 formMeta 生成 // 表单渲染通过 formMeta 生成
form?.render() form?.render()

View File

@ -56,6 +56,15 @@ function BaseNode() {
端口最终通过 `WorkflowPortRender` 组件渲染,支持自定义 style, 或者业务基于源码重新实现该组件, 参考 [自由布局最佳实践 - 节点渲染](https://github.com/bytedance/flowgram.ai/blob/main/apps/demo-free-layout/src/components/base-node/node-wrapper.tsx) 端口最终通过 `WorkflowPortRender` 组件渲染,支持自定义 style, 或者业务基于源码重新实现该组件, 参考 [自由布局最佳实践 - 节点渲染](https://github.com/bytedance/flowgram.ai/blob/main/apps/demo-free-layout/src/components/base-node/node-wrapper.tsx)
### 自定义端口颜色
可以通过向 `WorkflowPortRender` 传递颜色 props 来自定义端口颜色:
- `primaryColor` - 激活状态颜色linked/hovered
- `secondaryColor` - 默认状态颜色
- `errorColor` - 错误状态颜色
- `backgroundColor` - 背景颜色
```tsx pure ```tsx pure
import { WorkflowPortRender, useNodeRender } from '@flowgram.ai/free-layout-editor'; import { WorkflowPortRender, useNodeRender } from '@flowgram.ai/free-layout-editor';
@ -67,7 +76,17 @@ function BaseNode() {
<div data-port-id="condition-if-0" data-port-type="output"></div> <div data-port-id="condition-if-0" data-port-type="output"></div>
<div data-port-id="condition-if-1" data-port-type="output"></div> <div data-port-id="condition-if-1" data-port-type="output"></div>
{ports.map((p) => ( {ports.map((p) => (
<WorkflowPortRender key={p.id} entity={p} className="xxx" style={{ /* custom style */}}/> <WorkflowPortRender
key={p.id}
entity={p}
className="xxx"
style={{ /* custom style */}}
// 自定义端口颜色
primaryColor="#4d53e8" // 激活状态颜色linked/hovered
secondaryColor="#9197f1" // 默认状态颜色
errorColor="#ff4444" // 错误状态颜色
backgroundColor="#ffffff" // 背景颜色
/>
))} ))}
</div> </div>
) )

View File

@ -24,12 +24,12 @@ export interface LineColor {
} }
export enum LineColors { export enum LineColors {
HIDDEN = 'var(--g-line-color-hidden,transparent)', // 隐藏线条 HIDDEN = 'var(--g-workflow-line-color-hidden,transparent)', // 隐藏线条
DEFUALT = 'var(--g-line-color-default,#4d53e8)', DEFUALT = 'var(--g-workflow-line-color-default,#4d53e8)',
DRAWING = 'var(--g-line-color-drawing, #5DD6E3)', // '#b5bbf8', // '#9197F1', DRAWING = 'var(--g-workflow-line-color-drawing, #5DD6E3)', // '#b5bbf8', // '#9197F1',
HOVER = 'var(--g-line-color-hover,#37d0ff)', HOVER = 'var(--g-workflow-line-color-hover,#37d0ff)',
SELECTED = 'var(--g-line-color-selected,#37d0ff)', SELECTED = 'var(--g-workflow-line-color-selected,#37d0ff)',
ERROR = 'var(--g-line-color-error,red)', ERROR = 'var(--g-workflow-line-color-error,red)',
} }
export interface WorkflowLineRenderContribution { export interface WorkflowLineRenderContribution {

View File

@ -19,6 +19,14 @@ export interface WorkflowNodeProps {
port: WorkflowPortEntity, port: WorkflowPortEntity,
e: React.MouseEvent<HTMLDivElement> | React.MouseEventHandler<HTMLDivElement> e: React.MouseEvent<HTMLDivElement> | React.MouseEventHandler<HTMLDivElement>
) => void; ) => void;
/** 端口激活状态颜色 (linked/hovered) */
portPrimaryColor?: string;
/** 端口默认状态颜色 */
portSecondaryColor?: string;
/** 端口错误状态颜色 */
portErrorColor?: string;
/** 端口背景颜色 */
portBackgroundColor?: string;
} }
export const WorkflowNodeRenderer: React.FC<WorkflowNodeProps> = (props) => { export const WorkflowNodeRenderer: React.FC<WorkflowNodeProps> = (props) => {
@ -50,6 +58,10 @@ export const WorkflowNodeRenderer: React.FC<WorkflowNodeProps> = (props) => {
onClick={props.onPortClick ? (e) => props.onPortClick!(p, e) : undefined} onClick={props.onPortClick ? (e) => props.onPortClick!(p, e) : undefined}
className={props.portClassName} className={props.portClassName}
style={props.portStyle} style={props.portStyle}
primaryColor={props.portPrimaryColor}
secondaryColor={props.portSecondaryColor}
errorColor={props.portErrorColor}
backgroundColor={props.portBackgroundColor}
/> />
))} ))}
</> </>

View File

@ -19,6 +19,14 @@ export interface WorkflowPortRenderProps {
className?: string; className?: string;
style?: React.CSSProperties; style?: React.CSSProperties;
onClick?: React.MouseEventHandler<HTMLDivElement>; onClick?: React.MouseEventHandler<HTMLDivElement>;
/** 激活状态颜色 (linked/hovered) */
primaryColor?: string;
/** 默认状态颜色 */
secondaryColor?: string;
/** 错误状态颜色 */
errorColor?: string;
/** 背景颜色 */
backgroundColor?: string;
} }
export const WorkflowPortRender: React.FC<WorkflowPortRenderProps> = export const WorkflowPortRender: React.FC<WorkflowPortRenderProps> =
@ -79,10 +87,30 @@ export const WorkflowPortRender: React.FC<WorkflowPortRenderProps> =
// 有线条链接的时候深蓝色小圆点 // 有线条链接的时候深蓝色小圆点
linked, linked,
}); });
// 构建 CSS 自定义属性用于颜色覆盖
const colorStyles: Record<string, string> = {};
if (props.primaryColor) {
colorStyles['--g-workflow-port-color-primary'] = props.primaryColor;
}
if (props.secondaryColor) {
colorStyles['--g-workflow-port-color-secondary'] = props.secondaryColor;
}
if (props.errorColor) {
colorStyles['--g-workflow-port-color-error'] = props.errorColor;
}
if (props.backgroundColor) {
colorStyles['--g-workflow-port-color-background'] = props.backgroundColor;
}
const combinedStyle = targetElement
? { ...props.style, ...colorStyles }
: { ...props.style, ...colorStyles, left: posX, top: posY };
const content = ( const content = (
<WorkflowPointStyle <WorkflowPointStyle
className={className} className={className}
style={targetElement ? props.style : { ...props.style, left: posX, top: posY }} style={combinedStyle}
onClick={onClick} onClick={onClick}
data-port-entity-id={entity.id} data-port-entity-id={entity.id}
data-port-entity-type={entity.portType} data-port-entity-type={entity.portType}