fix: demo lint

This commit is contained in:
dragooncjw 2025-03-04 15:01:48 +08:00
parent e01601b733
commit b362b85141
50 changed files with 235 additions and 187 deletions

View File

@ -7,4 +7,9 @@ module.exports = defineConfig({
'no-console': 'off', 'no-console': 'off',
'react/prop-types': 'off', 'react/prop-types': 'off',
}, },
settings: {
react: {
version: 'detect', // 自动检测 React 版本
},
},
}); });

View File

@ -1,7 +1,6 @@
import { FlowMinimapService, MinimapRender } from '@flowgram.ai/minimap-plugin'; import { FlowMinimapService, MinimapRender } from '@flowgram.ai/minimap-plugin';
import { useService } from '@flowgram.ai/fixed-layout-editor'; import { useService } from '@flowgram.ai/fixed-layout-editor';
export const Minimap = () => { export const Minimap = () => {
const minimapService = useService(FlowMinimapService); const minimapService = useService(FlowMinimapService);
return ( return (

View File

@ -1,4 +1,5 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react';
import { usePlaygroundTools, useClientContext } from '@flowgram.ai/fixed-layout-editor'; import { usePlaygroundTools, useClientContext } from '@flowgram.ai/fixed-layout-editor';
export function Tools() { export function Tools() {
@ -15,13 +16,21 @@ export function Tools() {
return () => disposable.dispose(); return () => disposable.dispose();
}, [history]); }, [history]);
return <div style={{ position: 'absolute', zIndex: 10, bottom: 16, left: 16, display: 'flex', gap: 8 }}> return (
<div
style={{ position: 'absolute', zIndex: 10, bottom: 16, left: 16, display: 'flex', gap: 8 }}
>
<button onClick={() => tools.zoomin()}>ZoomIn</button> <button onClick={() => tools.zoomin()}>ZoomIn</button>
<button onClick={() => tools.zoomout()}>ZoomOut</button> <button onClick={() => tools.zoomout()}>ZoomOut</button>
<button onClick={() => tools.fitView()}>Fitview</button> <button onClick={() => tools.fitView()}>Fitview</button>
<button onClick={() => tools.changeLayout()}>ChangeLayout</button> <button onClick={() => tools.changeLayout()}>ChangeLayout</button>
<button onClick={() => history.undo()} disabled={!canUndo}>Undo</button> <button onClick={() => history.undo()} disabled={!canUndo}>
<button onClick={() => history.redo()} disabled={!canRedo}>Redo</button> Undo
</button>
<button onClick={() => history.redo()} disabled={!canRedo}>
Redo
</button>
<span>{Math.floor(tools.zoom * 100)}%</span> <span>{Math.floor(tools.zoom * 100)}%</span>
</div> </div>
);
} }

View File

@ -1,13 +1,13 @@
import { FixedLayoutEditorProvider, EditorRenderer } from '@flowgram.ai/fixed-layout-editor'; import { FixedLayoutEditorProvider, EditorRenderer } from '@flowgram.ai/fixed-layout-editor';
import '@flowgram.ai/fixed-layout-editor/index.css'; import '@flowgram.ai/fixed-layout-editor/index.css';
import './index.css' import './index.css';
import { nodeRegistries } from './node-registries';
import { initialData } from './initial-data';
import { useEditorProps } from './hooks/use-editor-props'; import { useEditorProps } from './hooks/use-editor-props';
import { initialData } from './initial-data' import { Tools } from './components/tools';
import { nodeRegistries } from './node-registries' import { Minimap } from './components/minimap';
import { Tools } from './components/tools'
import { Minimap } from './components/minimap'
export const Editor = () => { export const Editor = () => {
const editorProps = useEditorProps(initialData, nodeRegistries); const editorProps = useEditorProps(initialData, nodeRegistries);

View File

@ -11,7 +11,7 @@ export const initialData: FlowDocumentJSON = {
type: 'start', type: 'start',
data: { data: {
title: 'Start', title: 'Start',
content: 'start content' content: 'start content',
}, },
blocks: [], blocks: [],
}, },
@ -20,7 +20,7 @@ export const initialData: FlowDocumentJSON = {
id: 'condition_0', id: 'condition_0',
type: 'condition', type: 'condition',
data: { data: {
title: 'Condition' title: 'Condition',
}, },
blocks: [ blocks: [
{ {
@ -28,7 +28,7 @@ export const initialData: FlowDocumentJSON = {
type: 'block', type: 'block',
data: { data: {
title: 'Branch 0', title: 'Branch 0',
content: 'branch 1 content' content: 'branch 1 content',
}, },
blocks: [ blocks: [
{ {
@ -36,7 +36,7 @@ export const initialData: FlowDocumentJSON = {
type: 'custom', type: 'custom',
data: { data: {
title: 'Custom', title: 'Custom',
content: 'custrom content' content: 'custrom content',
}, },
}, },
], ],
@ -46,7 +46,7 @@ export const initialData: FlowDocumentJSON = {
type: 'block', type: 'block',
data: { data: {
title: 'Branch 1', title: 'Branch 1',
content: 'branch 1 content' content: 'branch 1 content',
}, },
blocks: [], blocks: [],
}, },
@ -58,9 +58,8 @@ export const initialData: FlowDocumentJSON = {
type: 'end', type: 'end',
data: { data: {
title: 'End', title: 'End',
content: 'end content' content: 'end content',
}, },
}, },
], ],
}; };

View File

@ -1,5 +1,5 @@
import { FlowNodeRegistry } from '@flowgram.ai/fixed-layout-editor';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { FlowNodeRegistry } from '@flowgram.ai/fixed-layout-editor';
/** /**
* *
@ -67,9 +67,9 @@ export const nodeRegistries: FlowNodeRegistry[] = [
type: 'custom', type: 'custom',
data: { data: {
title: 'Custom', title: 'Custom',
content: 'this is custom content' content: 'this is custom content',
} },
} };
} },
} },
]; ];

View File

@ -7,4 +7,9 @@ module.exports = defineConfig({
'no-console': 'off', 'no-console': 'off',
'react/prop-types': 'off', 'react/prop-types': 'off',
}, },
settings: {
react: {
version: 'detect', // 自动检测 React 版本
},
},
}); });

View File

@ -3,7 +3,7 @@ import styled from 'styled-components';
export const Container = styled.div<{ activated?: boolean; isVertical: boolean }>` export const Container = styled.div<{ activated?: boolean; isVertical: boolean }>`
width: 28px; width: 28px;
height: 18px; height: 18px;
background: ${props => (props.activated ? '#82A7FC' : 'rgb(187, 191, 196)')}; background: ${(props) => (props.activated ? '#82A7FC' : 'rgb(187, 191, 196)')};
display: flex; display: flex;
border-radius: 9px; border-radius: 9px;
justify-content: space-evenly; justify-content: space-evenly;
@ -11,7 +11,7 @@ export const Container = styled.div<{ activated?: boolean; isVertical: boolean }
color: #fff; color: #fff;
font-size: 10px; font-size: 10px;
font-weight: bold; font-weight: bold;
transform: ${props => (props.isVertical ? '' : 'rotate(90deg)')}; transform: ${(props) => (props.isVertical ? '' : 'rotate(90deg)')};
div { div {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -12,14 +12,14 @@ export interface PropsType {
export function DragNode(props: PropsType): JSX.Element { export function DragNode(props: PropsType): JSX.Element {
const { dragStart, dragNodes } = props; const { dragStart, dragNodes } = props;
const icon = FlowNodeRegistries.find(registry => registry.type === dragStart?.flowNodeType)?.info const icon = FlowNodeRegistries.find((registry) => registry.type === dragStart?.flowNodeType)
?.icon; ?.info?.icon;
const dragLength = (dragNodes || []) const dragLength = (dragNodes || [])
.map(_node => .map((_node) =>
_node.allCollapsedChildren.length _node.allCollapsedChildren.length
? _node.allCollapsedChildren.filter(_n => !_n.hidden).length ? _node.allCollapsedChildren.filter((_n) => !_n.hidden).length
: 1, : 1
) )
.reduce((acm, curr) => acm + curr, 0); .reduce((acm, curr) => acm + curr, 0);

View File

@ -15,7 +15,7 @@ const generateNewIdForChildren = (n: FlowNodeEntity): FlowNodeEntity => {
return { return {
...n, ...n,
id: generateNodeId(n), id: generateNodeId(n),
blocks: n.blocks.map(b => generateNewIdForChildren(b)), blocks: n.blocks.map((b) => generateNewIdForChildren(b)),
} as FlowNodeEntity; } as FlowNodeEntity;
} else { } else {
return { return {
@ -39,7 +39,7 @@ export default function Adder(props: {
const activated = useMemo( const activated = useMemo(
() => props.hoverActivated && !playground.config.readonly, () => props.hoverActivated && !playground.config.readonly,
[props.hoverActivated, playground.config.readonly], [props.hoverActivated, playground.config.readonly]
); );
const add = (addProps: any) => { const add = (addProps: any) => {
@ -111,7 +111,7 @@ export default function Adder(props: {
} }
: {} : {}
} }
onMouseDown={e => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
> >
{props.hoverActivated ? ( {props.hoverActivated ? (
<IconPlusCircle <IconPlusCircle

View File

@ -55,7 +55,7 @@ export function NodeList(props: { onSelect: (meta: any) => void; from: FlowNodeE
}; };
return ( return (
<NodesWrap style={{ width: 80 * 2 + 20 }}> <NodesWrap style={{ width: 80 * 2 + 20 }}>
{FlowNodeRegistries.map(registry => ( {FlowNodeRegistries.map((registry) => (
<Node <Node
key={registry.type} key={registry.type}
disabled={!(registry.canAdd?.(context, props.from) ?? true)} disabled={!(registry.canAdd?.(context, props.from) ?? true)}

View File

@ -40,7 +40,7 @@ export const SelectorBoxPopover: FunctionComponent<SelectorBoxPopoverProps> = ({
return false; return false;
} }
const findGroupInNodes = (nodes: FlowNodeEntity[]): boolean => const findGroupInNodes = (nodes: FlowNodeEntity[]): boolean =>
nodes.some(node => { nodes.some((node) => {
if (node.flowNodeType === FlowNodeBaseType.GROUP) { if (node.flowNodeType === FlowNodeBaseType.GROUP) {
return true; return true;
} }
@ -63,7 +63,7 @@ export const SelectorBoxPopover: FunctionComponent<SelectorBoxPopoverProps> = ({
top: bounds.top, top: bounds.top,
transform: 'translate(-100%, -100%)', transform: 'translate(-100%, -100%)',
}} }}
onMouseDown={e => { onMouseDown={(e) => {
e.stopPropagation(); e.stopPropagation();
}} }}
> >
@ -78,7 +78,7 @@ export const SelectorBoxPopover: FunctionComponent<SelectorBoxPopoverProps> = ({
icon={<IconHandle />} icon={<IconHandle />}
type="primary" type="primary"
theme="solid" theme="solid"
onMouseDown={e => { onMouseDown={(e) => {
e.stopPropagation(); e.stopPropagation();
startDrag(e, { startDrag(e, {
dragStartEntity: selectNodes[0], dragStartEntity: selectNodes[0],
@ -95,7 +95,7 @@ export const SelectorBoxPopover: FunctionComponent<SelectorBoxPopoverProps> = ({
style={{ height: BUTTON_HEIGHT }} style={{ height: BUTTON_HEIGHT }}
type="primary" type="primary"
theme="solid" theme="solid"
onMouseDown={e => { onMouseDown={(e) => {
commandRegistry.executeCommand(FlowCommandId.COLLAPSE); commandRegistry.executeCommand(FlowCommandId.COLLAPSE);
}} }}
/> />
@ -107,7 +107,7 @@ export const SelectorBoxPopover: FunctionComponent<SelectorBoxPopoverProps> = ({
style={{ height: BUTTON_HEIGHT }} style={{ height: BUTTON_HEIGHT }}
type="primary" type="primary"
theme="solid" theme="solid"
onMouseDown={e => { onMouseDown={(e) => {
commandRegistry.executeCommand(FlowCommandId.EXPAND); commandRegistry.executeCommand(FlowCommandId.EXPAND);
}} }}
/> />
@ -155,7 +155,7 @@ export const SelectorBoxPopover: FunctionComponent<SelectorBoxPopoverProps> = ({
</div> </div>
<div <div
style={{ cursor: draggable ? 'grab' : 'auto' }} style={{ cursor: draggable ? 'grab' : 'auto' }}
onMouseDown={e => { onMouseDown={(e) => {
e.stopPropagation(); e.stopPropagation();
startDrag(e, { startDrag(e, {
dragStartEntity: selectNodes[0], dragStartEntity: selectNodes[0],

View File

@ -1,10 +1,6 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { import { usePlayground, usePlaygroundTools, useRefresh } from '@flowgram.ai/fixed-layout-editor';
usePlayground,
usePlaygroundTools,
useRefresh,
} from '@flowgram.ai/fixed-layout-editor';
import { Tooltip, IconButton } from '@douyinfe/semi-ui'; import { Tooltip, IconButton } from '@douyinfe/semi-ui';
import { IconUndo, IconRedo } from '@douyinfe/semi-icons'; import { IconUndo, IconRedo } from '@douyinfe/semi-icons';

View File

@ -1,10 +1,6 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { import { useClientContext, getNodeForm, FlowNodeEntity } from '@flowgram.ai/fixed-layout-editor';
useClientContext,
getNodeForm,
FlowNodeEntity,
} from '@flowgram.ai/fixed-layout-editor';
import { Button, Badge } from '@douyinfe/semi-ui'; import { Button, Badge } from '@douyinfe/semi-ui';
export function Save(props: { disabled: boolean }) { export function Save(props: { disabled: boolean }) {
@ -12,8 +8,8 @@ export function Save(props: { disabled: boolean }) {
const clientContext = useClientContext(); const clientContext = useClientContext();
const updateValidateData = useCallback(() => { const updateValidateData = useCallback(() => {
const allForms = clientContext.document.getAllNodes().map(node => getNodeForm(node)); const allForms = clientContext.document.getAllNodes().map((node) => getNodeForm(node));
const count = allForms.filter(form => form?.state.invalid).length; const count = allForms.filter((form) => form?.state.invalid).length;
setErrorCount(count); setErrorCount(count);
}, [clientContext]); }, [clientContext]);
@ -21,8 +17,8 @@ export function Save(props: { disabled: boolean }) {
* Validate all node and Save * Validate all node and Save
*/ */
const onSave = useCallback(async () => { const onSave = useCallback(async () => {
const allForms = clientContext.document.getAllNodes().map(node => getNodeForm(node)); const allForms = clientContext.document.getAllNodes().map((node) => getNodeForm(node));
await Promise.all(allForms.map(async form => form?.validate())); await Promise.all(allForms.map(async (form) => form?.validate()));
console.log('>>>>> save data: ', clientContext.document.toJSON()); console.log('>>>>> save data: ', clientContext.document.toJSON());
}, [clientContext]); }, [clientContext]);
@ -37,9 +33,9 @@ export function Save(props: { disabled: boolean }) {
node.onDispose(() => formValidateDispose.dispose()); node.onDispose(() => formValidateDispose.dispose());
} }
}; };
clientContext.document.getAllNodes().map(node => listenSingleNodeValidate(node)); clientContext.document.getAllNodes().map((node) => listenSingleNodeValidate(node));
const dispose = clientContext.document.onNodeCreate(({ node }) => const dispose = clientContext.document.onNodeCreate(({ node }) =>
listenSingleNodeValidate(node), listenSingleNodeValidate(node)
); );
return () => dispose.dispose(); return () => dispose.dispose();
}, [clientContext]); }, [clientContext]);

View File

@ -19,7 +19,7 @@ const Warning = styled.span`
export const Feedback = ({ errors, warnings }: StatePanelProps) => { export const Feedback = ({ errors, warnings }: StatePanelProps) => {
const renderFeedbacks = (fs: FieldError[] | FieldWarning[] | undefined) => { const renderFeedbacks = (fs: FieldError[] | FieldWarning[] | undefined) => {
if (!fs) return null; if (!fs) return null;
return fs.map(f => <span key={f.name}>{f.message}</span>); return fs.map((f) => <span key={f.name}>{f.message}</span>);
}; };
return ( return (
<div> <div>

View File

@ -41,7 +41,7 @@ export function FormHeader() {
return ( return (
<Header <Header
onMouseDown={e => { onMouseDown={(e) => {
// trigger drag node // trigger drag node
startDrag(e); startDrag(e);
e.stopPropagation(); e.stopPropagation();
@ -73,7 +73,7 @@ export function FormHeader() {
size="small" size="small"
theme="borderless" theme="borderless"
icon={<IconMore />} icon={<IconMore />}
onClick={e => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
/> />
</Dropdown> </Dropdown>
</Operators> </Operators>

View File

@ -18,7 +18,7 @@ export function FormInputs() {
if (!properties) { if (!properties) {
return <></>; return <></>;
} }
const content = Object.keys(properties).map(key => { const content = Object.keys(properties).map((key) => {
const property = properties[key]; const property = properties[key];
return ( return (
<Field key={key} name={`inputsValues.${key}`} defaultValue={property.default}> <Field key={key} name={`inputsValues.${key}`} defaultValue={property.default}>

View File

@ -32,7 +32,7 @@ export function FormItem({
{required && <span style={{ color: '#f93920', paddingLeft: '2px' }}>*</span>} {required && <span style={{ color: '#f93920', paddingLeft: '2px' }}>*</span>}
</div> </div>
), ),
[], []
); );
return ( return (
<div <div

View File

@ -10,7 +10,7 @@ export function FormOutputs() {
{({ field }) => { {({ field }) => {
const properties = field.value?.properties; const properties = field.value?.properties;
if (properties) { if (properties) {
const content = Object.keys(properties).map(key => { const content = Object.keys(properties).map((key) => {
const property = properties[key]; const property = properties[key];
return <TypeTag key={key} name={key} type={property.type as string} />; return <TypeTag key={key} name={key} type={property.type as string} />;
}); });

View File

@ -13,7 +13,7 @@ export interface PropertiesEditProps {
useFx?: boolean; useFx?: boolean;
} }
export const PropertiesEdit: React.FC<PropertiesEditProps> = props => { export const PropertiesEdit: React.FC<PropertiesEditProps> = (props) => {
const value = (props.value || {}) as Record<string, JsonSchema>; const value = (props.value || {}) as Record<string, JsonSchema>;
const { readonly } = useContext(NodeRenderContext); const { readonly } = useContext(NodeRenderContext);
const [newProperty, updateNewPropertyFromCache] = useState<{ key: string; value: JsonSchema }>({ const [newProperty, updateNewPropertyFromCache] = useState<{ key: string; value: JsonSchema }>({
@ -28,7 +28,7 @@ export const PropertiesEdit: React.FC<PropertiesEditProps> = props => {
const updateProperty = ( const updateProperty = (
propertyValue: JsonSchema, propertyValue: JsonSchema,
propertyKey: string, propertyKey: string,
newPropertyKey?: string, newPropertyKey?: string
) => { ) => {
const newValue = { ...value }; const newValue = { ...value };
if (newPropertyKey) { if (newPropertyKey) {
@ -42,7 +42,7 @@ export const PropertiesEdit: React.FC<PropertiesEditProps> = props => {
const updateNewProperty = ( const updateNewProperty = (
propertyValue: JsonSchema, propertyValue: JsonSchema,
propertyKey: string, propertyKey: string,
newPropertyKey?: string, newPropertyKey?: string
) => { ) => {
// const newValue = { ...value } // const newValue = { ...value }
if (newPropertyKey) { if (newPropertyKey) {
@ -59,7 +59,7 @@ export const PropertiesEdit: React.FC<PropertiesEditProps> = props => {
}; };
return ( return (
<> <>
{Object.keys(props.value || {}).map(key => { {Object.keys(props.value || {}).map((key) => {
const property = (value[key] || {}) as JsonSchema; const property = (value[key] || {}) as JsonSchema;
return ( return (
<PropertyEdit <PropertyEdit

View File

@ -19,7 +19,7 @@ interface GroupNoteProps {
enableTooltip?: boolean; enableTooltip?: boolean;
} }
export const GroupNote: FC<GroupNoteProps> = props => { export const GroupNote: FC<GroupNoteProps> = (props) => {
const { const {
groupController, groupController,
containerStyle = {}, containerStyle = {},
@ -65,14 +65,14 @@ export const GroupNote: FC<GroupNoteProps> = props => {
> >
<MultiLineEditor <MultiLineEditor
value={editingValue} value={editingValue}
onChange={note => { onChange={(note) => {
setEditingValue(note || ''); setEditingValue(note || '');
}} }}
readonly={playground.config.readonly} readonly={playground.config.readonly}
placeholder="Please enter note" placeholder="Please enter note"
style={textStyle} style={textStyle}
autoSize={autoSize} autoSize={autoSize}
onEditingChange={editingState => { onEditingChange={(editingState) => {
if (editingState) { if (editingState) {
setTooltipVisible(false); setTooltipVisible(false);
} }

View File

@ -29,7 +29,7 @@ interface GroupToolsProps {
const BUTTON_HEIGHT = 24; const BUTTON_HEIGHT = 24;
export const GroupTools: FC<GroupToolsProps> = props => { export const GroupTools: FC<GroupToolsProps> = (props) => {
const { groupNode, groupController, visible, style = {} } = props; const { groupNode, groupController, visible, style = {} } = props;
const groupService = useService<FlowGroupService>(FlowGroupService); const groupService = useService<FlowGroupService>(FlowGroupService);
@ -53,7 +53,7 @@ export const GroupTools: FC<GroupToolsProps> = props => {
color: 'rgb(97, 69, 211)', color: 'rgb(97, 69, 211)',
...style, ...style,
}} }}
onMouseDown={e => { onMouseDown={(e) => {
e.stopPropagation(); e.stopPropagation();
}} }}
> >
@ -64,7 +64,7 @@ export const GroupTools: FC<GroupToolsProps> = props => {
icon={<IconHandle />} icon={<IconHandle />}
type="primary" type="primary"
theme="borderless" theme="borderless"
onMouseDown={e => { onMouseDown={(e) => {
e.stopPropagation(); e.stopPropagation();
startDrag(e, { startDrag(e, {
dragStartEntity: groupNode, dragStartEntity: groupNode,
@ -80,7 +80,7 @@ export const GroupTools: FC<GroupToolsProps> = props => {
icon={groupController?.collapsed ? <IconExpand /> : <IconShrink />} icon={groupController?.collapsed ? <IconExpand /> : <IconShrink />}
type="primary" type="primary"
theme="borderless" theme="borderless"
onClick={e => { onClick={(e) => {
if (!groupController) { if (!groupController) {
return; return;
} }

View File

@ -15,7 +15,7 @@ interface Props {
[key: string]: any; [key: string]: any;
} }
const BaseTextarea: React.FC<Props> = props => { const BaseTextarea: React.FC<Props> = (props) => {
const { value, onChange, onBlur, editing, onFocus, autoSize = true, ...rest } = props; const { value, onChange, onBlur, editing, onFocus, autoSize = true, ...rest } = props;
const [data, setData] = useState(value); const [data, setData] = useState(value);
@ -46,7 +46,7 @@ const BaseTextarea: React.FC<Props> = props => {
{...rest} {...rest}
ref={textareaRef} ref={textareaRef}
value={data} value={data}
onChange={v => { onChange={(v) => {
setData(v); setData(v);
}} }}
onEnterPress={onSubmit} onEnterPress={onSubmit}

View File

@ -21,7 +21,7 @@ interface Props {
onEditingChange?: (editing: boolean) => void; onEditingChange?: (editing: boolean) => void;
} }
const MultiLineEditor: React.FC<Props> = props => { const MultiLineEditor: React.FC<Props> = (props) => {
const { const {
value, value,
onChange, onChange,

View File

@ -14,27 +14,27 @@ import { writeData } from './utils';
import { FlowCommandId } from './constants'; import { FlowCommandId } from './constants';
type ShortcutGetter = ( type ShortcutGetter = (
ctx: FixedLayoutPluginContext, ctx: FixedLayoutPluginContext
) => Parameters<ShortcutsRegistry['addHandlers']>[0]; ) => Parameters<ShortcutsRegistry['addHandlers']>[0];
const copy: ShortcutGetter = ctx => { const copy: ShortcutGetter = (ctx) => {
const selection = ctx.selection; const selection = ctx.selection;
const clipboard = ctx.clipboard; const clipboard = ctx.clipboard;
return { return {
commandId: FlowCommandId.COPY, commandId: FlowCommandId.COPY,
shortcuts: ['meta c', 'ctrl c'], shortcuts: ['meta c', 'ctrl c'],
isEnabled: node => isEnabled: (node) =>
(selection?.selection.length > 0 || node instanceof FlowNodeEntity) && (selection?.selection.length > 0 || node instanceof FlowNodeEntity) &&
!ctx.playground.config.readonlyOrDisabled, !ctx.playground.config.readonlyOrDisabled,
execute: node => { execute: (node) => {
const nodes = const nodes =
node instanceof FlowNodeEntity node instanceof FlowNodeEntity
? [node] ? [node]
: (selection.selection.filter( : (selection.selection.filter(
_entity => _entity instanceof FlowNodeEntity, (_entity) => _entity instanceof FlowNodeEntity
) as FlowNodeEntity[]); ) as FlowNodeEntity[]);
const originNodes = nodes.map(n => ({ const originNodes = nodes.map((n) => ({
...n.toJSON(), ...n.toJSON(),
id: `${n.flowNodeType}_${nanoid()}`, id: `${n.flowNodeType}_${nanoid()}`,
})); }));
@ -47,7 +47,7 @@ const copy: ShortcutGetter = ctx => {
}; };
}; };
const cut: ShortcutGetter = ctx => { const cut: ShortcutGetter = (ctx) => {
const selection = ctx.selection; const selection = ctx.selection;
const commandRegistry = ctx.get<CommandRegistry>(CommandRegistry); const commandRegistry = ctx.get<CommandRegistry>(CommandRegistry);
@ -75,7 +75,7 @@ const cut: ShortcutGetter = ctx => {
}; };
}; };
const zoomIn: ShortcutGetter = ctx => { const zoomIn: ShortcutGetter = (ctx) => {
const config = ctx.playground.config; const config = ctx.playground.config;
return { return {
@ -87,7 +87,7 @@ const zoomIn: ShortcutGetter = ctx => {
}; };
}; };
const zoomOut: ShortcutGetter = ctx => { const zoomOut: ShortcutGetter = (ctx) => {
const config = ctx.playground.config; const config = ctx.playground.config;
return { return {
@ -99,7 +99,7 @@ const zoomOut: ShortcutGetter = ctx => {
}; };
}; };
const resetZoom: ShortcutGetter = ctx => ({ const resetZoom: ShortcutGetter = (ctx) => ({
commandId: FlowCommandId.RESET_ZOOM, commandId: FlowCommandId.RESET_ZOOM,
commandDetail: { commandDetail: {
label: 'Reset Zoom', label: 'Reset Zoom',
@ -110,7 +110,7 @@ const resetZoom: ShortcutGetter = ctx => ({
}, },
}); });
const group: ShortcutGetter = ctx => ({ const group: ShortcutGetter = (ctx) => ({
commandId: FlowCommandId.GROUP, commandId: FlowCommandId.GROUP,
commandDetail: { commandDetail: {
label: 'Create Group', label: 'Create Group',
@ -123,14 +123,14 @@ const group: ShortcutGetter = ctx => ({
const selection = ctx.playground.selectionService; const selection = ctx.playground.selectionService;
groupService.createGroup( groupService.createGroup(
selection.selection.filter(_entity => _entity instanceof FlowNodeEntity) as FlowNodeEntity[], selection.selection.filter((_entity) => _entity instanceof FlowNodeEntity) as FlowNodeEntity[]
); );
ctx.playground.selectionService.selection = []; ctx.playground.selectionService.selection = [];
}, },
}); });
const selectAll: ShortcutGetter = ctx => ({ const selectAll: ShortcutGetter = (ctx) => ({
commandId: FlowCommandId.SELECT_ALL, commandId: FlowCommandId.SELECT_ALL,
commandDetail: { commandDetail: {
label: 'Select All', label: 'Select All',
@ -139,14 +139,14 @@ const selectAll: ShortcutGetter = ctx => ({
isEnabled: () => !ctx.playground.config.readonlyOrDisabled, isEnabled: () => !ctx.playground.config.readonlyOrDisabled,
execute: () => { execute: () => {
const allNodes = (ctx.document.root.children || []).filter( const allNodes = (ctx.document.root.children || []).filter(
node => node.flowNodeType !== 'start' && node.flowNodeType !== 'end', (node) => node.flowNodeType !== 'start' && node.flowNodeType !== 'end'
); );
ctx.playground.selectionService.selection = allNodes; ctx.playground.selectionService.selection = allNodes;
}, },
}); });
const cancelSelect: ShortcutGetter = ctx => ({ const cancelSelect: ShortcutGetter = (ctx) => ({
commandId: FlowCommandId.CANCEL_SELECT, commandId: FlowCommandId.CANCEL_SELECT,
commandDetail: { commandDetail: {
label: 'Cancel Select', label: 'Cancel Select',
@ -157,7 +157,7 @@ const cancelSelect: ShortcutGetter = ctx => ({
}, },
}); });
const collapse: ShortcutGetter = ctx => ({ const collapse: ShortcutGetter = (ctx) => ({
commandId: FlowCommandId.COLLAPSE, commandId: FlowCommandId.COLLAPSE,
commandDetail: { commandDetail: {
label: 'Collapse', label: 'Collapse',
@ -168,19 +168,19 @@ const collapse: ShortcutGetter = ctx => ({
const selection = ctx.selection; const selection = ctx.selection;
const selectNodes = selection.selection.filter( const selectNodes = selection.selection.filter(
_entity => _entity instanceof FlowNodeEntity, (_entity) => _entity instanceof FlowNodeEntity
) as FlowNodeEntity[]; ) as FlowNodeEntity[];
selectNodes selectNodes
.map(_node => [_node, ..._node.allCollapsedChildren]) .map((_node) => [_node, ..._node.allCollapsedChildren])
.flat() .flat()
.forEach(node => { .forEach((node) => {
const renderData = node.getData(FlowNodeRenderData); const renderData = node.getData(FlowNodeRenderData);
if ( if (
node.firstChild && node.firstChild &&
[FlowNodeBaseType.BLOCK_ICON, FlowNodeBaseType.BLOCK_ORDER_ICON].includes( [FlowNodeBaseType.BLOCK_ICON, FlowNodeBaseType.BLOCK_ORDER_ICON].includes(
node.firstChild.flowNodeType as FlowNodeBaseType, node.firstChild.flowNodeType as FlowNodeBaseType
) )
) { ) {
node.collapsed = true; node.collapsed = true;
@ -191,7 +191,7 @@ const collapse: ShortcutGetter = ctx => ({
}, },
}); });
const expand: ShortcutGetter = ctx => ({ const expand: ShortcutGetter = (ctx) => ({
commandId: FlowCommandId.EXPAND, commandId: FlowCommandId.EXPAND,
commandDetail: { commandDetail: {
label: 'Expand', label: 'Expand',
@ -202,19 +202,19 @@ const expand: ShortcutGetter = ctx => ({
const selection = ctx.selection; const selection = ctx.selection;
const selectNodes = selection.selection.filter( const selectNodes = selection.selection.filter(
_entity => _entity instanceof FlowNodeEntity, (_entity) => _entity instanceof FlowNodeEntity
) as FlowNodeEntity[]; ) as FlowNodeEntity[];
selectNodes selectNodes
.map(_node => [_node, ..._node.allCollapsedChildren]) .map((_node) => [_node, ..._node.allCollapsedChildren])
.flat() .flat()
.forEach(node => { .forEach((node) => {
const renderData = node.getData(FlowNodeRenderData); const renderData = node.getData(FlowNodeRenderData);
if ( if (
node.firstChild && node.firstChild &&
[FlowNodeBaseType.BLOCK_ICON, FlowNodeBaseType.BLOCK_ORDER_ICON].includes( [FlowNodeBaseType.BLOCK_ICON, FlowNodeBaseType.BLOCK_ORDER_ICON].includes(
node.firstChild.flowNodeType as FlowNodeBaseType, node.firstChild.flowNodeType as FlowNodeBaseType
) )
) { ) {
node.collapsed = false; node.collapsed = false;

View File

@ -7,4 +7,9 @@ module.exports = defineConfig({
'no-console': 'off', 'no-console': 'off',
'react/prop-types': 'off', 'react/prop-types': 'off',
}, },
settings: {
react: {
version: 'detect', // 自动检测 React 版本
},
},
}); });

View File

@ -1,7 +1,6 @@
import { FlowMinimapService, MinimapRender } from '@flowgram.ai/minimap-plugin'; import { FlowMinimapService, MinimapRender } from '@flowgram.ai/minimap-plugin';
import { useService } from '@flowgram.ai/free-layout-editor'; import { useService } from '@flowgram.ai/free-layout-editor';
export const Minimap = () => { export const Minimap = () => {
const minimapService = useService(FlowMinimapService); const minimapService = useService(FlowMinimapService);
return ( return (

View File

@ -4,21 +4,23 @@ import { WorkflowDragService, useService } from '@flowgram.ai/free-layout-editor
const cardkeys = ['Node1', 'Node2']; const cardkeys = ['Node1', 'Node2'];
export const NodeAddPanel: React.FC = props => { export const NodeAddPanel: React.FC = (props) => {
const startDragSerivce = useService<WorkflowDragService>(WorkflowDragService); const startDragSerivce = useService<WorkflowDragService>(WorkflowDragService);
return ( return (
<div className="demo-free-sidebar"> <div className="demo-free-sidebar">
{cardkeys.map(nodeType => ( {cardkeys.map((nodeType) => (
<div <div
key={nodeType} key={nodeType}
className="demo-free-card" className="demo-free-card"
onMouseDown={e => startDragSerivce.startDragCard(nodeType, e, { onMouseDown={(e) =>
startDragSerivce.startDragCard(nodeType, e, {
data: { data: {
title: `New ${nodeType}`, title: `New ${nodeType}`,
content: 'xxxx' content: 'xxxx',
},
})
} }
})}
> >
{nodeType} {nodeType}
</div> </div>

View File

@ -1,4 +1,5 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react';
import { usePlaygroundTools, useClientContext } from '@flowgram.ai/free-layout-editor'; import { usePlaygroundTools, useClientContext } from '@flowgram.ai/free-layout-editor';
export function Tools() { export function Tools() {
@ -15,13 +16,21 @@ export function Tools() {
return () => disposable.dispose(); return () => disposable.dispose();
}, [history]); }, [history]);
return <div style={{ position: 'absolute', zIndex: 10, bottom: 16, left: 226, display: 'flex', gap: 8 }}> return (
<div
style={{ position: 'absolute', zIndex: 10, bottom: 16, left: 226, display: 'flex', gap: 8 }}
>
<button onClick={() => tools.zoomin()}>ZoomIn</button> <button onClick={() => tools.zoomin()}>ZoomIn</button>
<button onClick={() => tools.zoomout()}>ZoomOut</button> <button onClick={() => tools.zoomout()}>ZoomOut</button>
<button onClick={() => tools.fitView()}>Fitview</button> <button onClick={() => tools.fitView()}>Fitview</button>
<button onClick={() => tools.autoLayout()}>AutoLayout</button> <button onClick={() => tools.autoLayout()}>AutoLayout</button>
<button onClick={() => history.undo()} disabled={!canUndo}>Undo</button> <button onClick={() => history.undo()} disabled={!canUndo}>
<button onClick={() => history.redo()} disabled={!canRedo}>Redo</button> Undo
</button>
<button onClick={() => history.redo()} disabled={!canRedo}>
Redo
</button>
<span>{Math.floor(tools.zoom * 100)}%</span> <span>{Math.floor(tools.zoom * 100)}%</span>
</div> </div>
);
} }

View File

@ -1,17 +1,14 @@
import { import { EditorRenderer, FreeLayoutEditorProvider } from '@flowgram.ai/free-layout-editor';
EditorRenderer,
FreeLayoutEditorProvider,
} from '@flowgram.ai/free-layout-editor';
import { useEditorProps } from './hooks/use-editor-props';
import { Tools } from './components/tools';
import { NodeAddPanel } from './components/node-add-panel'; import { NodeAddPanel } from './components/node-add-panel';
import { Tools } from './components/tools' import { Minimap } from './components/minimap';
import { Minimap } from './components/minimap'
import { useEditorProps } from './hooks/use-editor-props'
import '@flowgram.ai/free-layout-editor/index.css'; import '@flowgram.ai/free-layout-editor/index.css';
import './index.css'; import './index.css';
export const Editor = () => { export const Editor = () => {
const editorProps = useEditorProps() const editorProps = useEditorProps();
return ( return (
<FreeLayoutEditorProvider {...editorProps}> <FreeLayoutEditorProvider {...editorProps}>
<div className="demo-free-container"> <div className="demo-free-container">
@ -23,5 +20,5 @@ export const Editor = () => {
<Minimap /> <Minimap />
</div> </div>
</FreeLayoutEditorProvider> </FreeLayoutEditorProvider>
) );
}; };

View File

@ -1,19 +1,20 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { createMinimapPlugin } from '@flowgram.ai/minimap-plugin';
import { createFreeSnapPlugin } from '@flowgram.ai/free-snap-plugin';
import { import {
FreeLayoutProps, FreeLayoutProps,
WorkflowNodeProps, WorkflowNodeProps,
WorkflowNodeRenderer, WorkflowNodeRenderer,
Field, Field,
useNodeRender useNodeRender,
} from '@flowgram.ai/free-layout-editor'; } from '@flowgram.ai/free-layout-editor';
import { createMinimapPlugin } from '@flowgram.ai/minimap-plugin';
import { createFreeSnapPlugin } from '@flowgram.ai/free-snap-plugin';
import { nodeRegistries } from '../node-registries';
import { initialData } from '../initial-data'; import { initialData } from '../initial-data';
import { nodeRegistries } from '../node-registries'
export const useEditorProps = () => useMemo<FreeLayoutProps>( export const useEditorProps = () =>
useMemo<FreeLayoutProps>(
() => ({ () => ({
/** /**
* Whether to enable the background * Whether to enable the background
@ -47,7 +48,8 @@ export const useEditorProps = () => useMemo<FreeLayoutProps>(
/** /**
* Render form * Render form
*/ */
render: () => <> render: () => (
<>
<Field<string> name="title"> <Field<string> name="title">
{({ field }) => <div className="demo-free-node-title">{field.value}</div>} {({ field }) => <div className="demo-free-node-title">{field.value}</div>}
</Field> </Field>
@ -57,7 +59,8 @@ export const useEditorProps = () => useMemo<FreeLayoutProps>(
</Field> </Field>
</div> </div>
</> </>
} ),
},
}; };
}, },
materials: { materials: {
@ -65,12 +68,12 @@ export const useEditorProps = () => useMemo<FreeLayoutProps>(
* Render Node * Render Node
*/ */
renderDefaultNode: (props: WorkflowNodeProps) => { renderDefaultNode: (props: WorkflowNodeProps) => {
const { form } = useNodeRender() const { form } = useNodeRender();
return ( return (
<WorkflowNodeRenderer className="demo-free-node" node={props.node}> <WorkflowNodeRenderer className="demo-free-node" node={props.node}>
{form?.render()} {form?.render()}
</WorkflowNodeRenderer> </WorkflowNodeRenderer>
) );
}, },
}, },
/** /**
@ -95,7 +98,7 @@ export const useEditorProps = () => useMemo<FreeLayoutProps>(
/** /**
* Playground init * Playground init
*/ */
onInit: ctx => {}, onInit: (ctx) => {},
/** /**
* Playground render * Playground render
*/ */
@ -146,7 +149,7 @@ export const useEditorProps = () => useMemo<FreeLayoutProps>(
alignLineWidth: 1, alignLineWidth: 1,
alignCrossWidth: 8, alignCrossWidth: 8,
}), }),
] ],
}), }),
[], []
); );

View File

@ -10,7 +10,7 @@ export const initialData: WorkflowJSON = {
}, },
data: { data: {
title: 'Start', title: 'Start',
content: 'Start content' content: 'Start content',
}, },
}, },
{ {
@ -21,7 +21,7 @@ export const initialData: WorkflowJSON = {
}, },
data: { data: {
title: 'Custom', title: 'Custom',
content: 'Custom node content' content: 'Custom node content',
}, },
}, },
{ {
@ -32,7 +32,7 @@ export const initialData: WorkflowJSON = {
}, },
data: { data: {
title: 'End', title: 'End',
content: 'End content' content: 'End content',
}, },
}, },
], ],
@ -47,4 +47,3 @@ export const initialData: WorkflowJSON = {
}, },
], ],
}; };

View File

@ -24,8 +24,7 @@ export const nodeRegistries: WorkflowNodeRegistry[] = [
}, },
{ {
type: 'custom', type: 'custom',
meta: { meta: {},
},
defaultPorts: [{ type: 'output' }, { type: 'input' }], // A normal node has two ports defaultPorts: [{ type: 'output' }, { type: 'input' }], // A normal node has two ports
}, },
]; ];

View File

@ -7,4 +7,9 @@ module.exports = defineConfig({
'no-console': 'off', 'no-console': 'off',
'react/prop-types': 'off', 'react/prop-types': 'off',
}, },
settings: {
react: {
version: 'detect', // 自动检测 React 版本
},
},
}); });

View File

@ -22,7 +22,7 @@ export const SelectorBoxPopover: FunctionComponent<SelectorBoxPopoverProps> = ({
top: bounds.top, top: bounds.top,
transform: 'translate(-100%, -100%)', transform: 'translate(-100%, -100%)',
}} }}
onMouseDown={e => { onMouseDown={(e) => {
e.stopPropagation(); e.stopPropagation();
}} }}
> >
@ -36,7 +36,7 @@ export const SelectorBoxPopover: FunctionComponent<SelectorBoxPopoverProps> = ({
style={{ height: BUTTON_HEIGHT }} style={{ height: BUTTON_HEIGHT }}
type="primary" type="primary"
theme="solid" theme="solid"
onMouseDown={e => { onMouseDown={(e) => {
commandRegistry.executeCommand(FlowCommandId.COLLAPSE); commandRegistry.executeCommand(FlowCommandId.COLLAPSE);
}} }}
/> />
@ -48,7 +48,7 @@ export const SelectorBoxPopover: FunctionComponent<SelectorBoxPopoverProps> = ({
style={{ height: BUTTON_HEIGHT }} style={{ height: BUTTON_HEIGHT }}
type="primary" type="primary"
theme="solid" theme="solid"
onMouseDown={e => { onMouseDown={(e) => {
commandRegistry.executeCommand(FlowCommandId.EXPAND); commandRegistry.executeCommand(FlowCommandId.EXPAND);
}} }}
/> />

View File

@ -4,6 +4,9 @@ import {
usePlaygroundTools, usePlaygroundTools,
type InteractiveType as IdeInteractiveType, type InteractiveType as IdeInteractiveType,
} from '@flowgram.ai/free-layout-editor'; } from '@flowgram.ai/free-layout-editor';
import { Tooltip, Popover } from '@douyinfe/semi-ui';
import { MousePadSelector } from './mouse-pad-selector';
export const CACHE_KEY = 'workflow_prefer_interactive_type'; export const CACHE_KEY = 'workflow_prefer_interactive_type';
export const SHOW_KEY = 'show_workflow_interactive_type_guide'; export const SHOW_KEY = 'show_workflow_interactive_type_guide';
@ -25,9 +28,6 @@ export enum InteractiveType {
Mouse = 'MOUSE', Mouse = 'MOUSE',
Pad = 'PAD', Pad = 'PAD',
} }
import { Tooltip, Popover } from '@douyinfe/semi-ui';
import { MousePadSelector } from './mouse-pad-selector';
export const Interactive = () => { export const Interactive = () => {
const tools = usePlaygroundTools(); const tools = usePlaygroundTools();

View File

@ -1,11 +1,11 @@
import { EditorRenderer, FreeLayoutEditorProvider } from '@flowgram.ai/free-layout-editor'; import { EditorRenderer, FreeLayoutEditorProvider } from '@flowgram.ai/free-layout-editor';
import { DemoTools } from './components/tools';
import '@flowgram.ai/free-layout-editor/index.css'; import '@flowgram.ai/free-layout-editor/index.css';
import './styles/index.css'; import './styles/index.css';
import { nodeRegistries } from './nodes'; import { nodeRegistries } from './nodes';
import { initialData } from './initial-data'; import { initialData } from './initial-data';
import { useEditorProps } from './hooks'; import { useEditorProps } from './hooks';
import { DemoTools } from './components/tools';
export const Editor = () => { export const Editor = () => {
const editorProps = useEditorProps(initialData, nodeRegistries); const editorProps = useEditorProps(initialData, nodeRegistries);

View File

@ -20,7 +20,7 @@ const Warning = styled.span`
export const Feedback = ({ errors, warnings, invalid }: StatePanelProps) => { export const Feedback = ({ errors, warnings, invalid }: StatePanelProps) => {
const renderFeedbacks = (fs: FieldError[] | FieldWarning[] | undefined) => { const renderFeedbacks = (fs: FieldError[] | FieldWarning[] | undefined) => {
if (!fs) return null; if (!fs) return null;
return fs.map(f => <span key={f.name}>{f.message}</span>); return fs.map((f) => <span key={f.name}>{f.message}</span>);
}; };
return ( return (
<div> <div>

View File

@ -71,7 +71,7 @@ export function FormHeader() {
size="small" size="small"
theme="borderless" theme="borderless"
icon={<IconMore />} icon={<IconMore />}
onClick={e => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
/> />
</Dropdown> </Dropdown>
</Operators> </Operators>

View File

@ -18,7 +18,7 @@ export function FormInputs() {
if (!properties) { if (!properties) {
return <></>; return <></>;
} }
const content = Object.keys(properties).map(key => { const content = Object.keys(properties).map((key) => {
const property = properties[key]; const property = properties[key];
return ( return (
<Field key={key} name={`inputsValues.${key}`} defaultValue={property.default}> <Field key={key} name={`inputsValues.${key}`} defaultValue={property.default}>

View File

@ -32,7 +32,7 @@ export function FormItem({
{required && <span style={{ color: '#f93920', paddingLeft: '2px' }}>*</span>} {required && <span style={{ color: '#f93920', paddingLeft: '2px' }}>*</span>}
</div> </div>
), ),
[], []
); );
return ( return (
<div <div

View File

@ -10,7 +10,7 @@ export function FormOutputs() {
{({ field }) => { {({ field }) => {
const properties = field.value?.properties; const properties = field.value?.properties;
if (properties) { if (properties) {
const content = Object.keys(properties).map(key => { const content = Object.keys(properties).map((key) => {
const property = properties[key]; const property = properties[key];
return <TypeTag key={key} name={key} type={property.type as string} />; return <TypeTag key={key} name={key} type={property.type as string} />;
}); });

View File

@ -13,7 +13,7 @@ export interface PropertiesEditProps {
useFx?: boolean; useFx?: boolean;
} }
export const PropertiesEdit: React.FC<PropertiesEditProps> = props => { export const PropertiesEdit: React.FC<PropertiesEditProps> = (props) => {
const value = (props.value || {}) as Record<string, JsonSchema>; const value = (props.value || {}) as Record<string, JsonSchema>;
const { readonly } = useContext(NodeRenderContext); const { readonly } = useContext(NodeRenderContext);
const [newProperty, updateNewPropertyFromCache] = useState<{ key: string; value: JsonSchema }>({ const [newProperty, updateNewPropertyFromCache] = useState<{ key: string; value: JsonSchema }>({
@ -28,7 +28,7 @@ export const PropertiesEdit: React.FC<PropertiesEditProps> = props => {
const updateProperty = ( const updateProperty = (
propertyValue: JsonSchema, propertyValue: JsonSchema,
propertyKey: string, propertyKey: string,
newPropertyKey?: string, newPropertyKey?: string
) => { ) => {
const newValue = { ...value }; const newValue = { ...value };
if (newPropertyKey) { if (newPropertyKey) {
@ -42,7 +42,7 @@ export const PropertiesEdit: React.FC<PropertiesEditProps> = props => {
const updateNewProperty = ( const updateNewProperty = (
propertyValue: JsonSchema, propertyValue: JsonSchema,
propertyKey: string, propertyKey: string,
newPropertyKey?: string, newPropertyKey?: string
) => { ) => {
// const newValue = { ...value } // const newValue = { ...value }
if (newPropertyKey) { if (newPropertyKey) {
@ -59,7 +59,7 @@ export const PropertiesEdit: React.FC<PropertiesEditProps> = props => {
}; };
return ( return (
<> <>
{Object.keys(props.value || {}).map(key => { {Object.keys(props.value || {}).map((key) => {
const property = (value[key] || {}) as JsonSchema; const property = (value[key] || {}) as JsonSchema;
return ( return (
<PropertyEdit <PropertyEdit

View File

@ -29,7 +29,7 @@ export function ConditionInputs() {
<FormItem name="if" type="boolean" required={true} labelWidth={40}> <FormItem name="if" type="boolean" required={true} labelWidth={40}>
<FxExpression <FxExpression
value={childField.value.value} value={childField.value.value}
onChange={v => childField.onChange({ key: childField.value.key, value: v })} onChange={(v) => childField.onChange({ key: childField.value.key, value: v })}
icon={ icon={
<Button <Button
theme="borderless" theme="borderless"

View File

@ -25,7 +25,7 @@ export function shortcuts(shortcutsRegistry: ShortcutsRegistry, ctx: FreeLayoutP
shortcutsRegistry.addHandlers({ shortcutsRegistry.addHandlers({
commandId: FlowCommandId.COPY, commandId: FlowCommandId.COPY,
shortcuts: ['meta c', 'ctrl c'], shortcuts: ['meta c', 'ctrl c'],
execute: async e => { execute: async (e) => {
const document = ctx.get<WorkflowDocument>(WorkflowDocument); const document = ctx.get<WorkflowDocument>(WorkflowDocument);
const selectService = ctx.get<WorkflowSelectService>(WorkflowSelectService); const selectService = ctx.get<WorkflowSelectService>(WorkflowSelectService);
@ -45,30 +45,30 @@ export function shortcuts(shortcutsRegistry: ShortcutsRegistry, ctx: FreeLayoutP
return; return;
} }
const nodeEntities = selectedNodes.filter( const nodeEntities = selectedNodes.filter(
n => n.flowNodeType !== 'start' && n.flowNodeType !== 'end', (n) => n.flowNodeType !== 'start' && n.flowNodeType !== 'end'
); );
const nodes = await Promise.all( const nodes = await Promise.all(
nodeEntities.map(async nodeEntity => { nodeEntities.map(async (nodeEntity) => {
const nodeJSON = await document.toNodeJSON(nodeEntity); const nodeJSON = await document.toNodeJSON(nodeEntity);
return { return {
nodeJSON, nodeJSON,
nodeType: nodeEntity.flowNodeType, nodeType: nodeEntity.flowNodeType,
}; };
}), })
); );
navigator.clipboard navigator.clipboard
.writeText( .writeText(
JSON.stringify({ JSON.stringify({
nodes, nodes,
fromHost: window.location.host, fromHost: window.location.host,
}), })
) )
.then(() => { .then(() => {
Toast.success({ Toast.success({
content: 'Nodes copied', content: 'Nodes copied',
}); });
}) })
.catch(err => { .catch((err) => {
Toast.error({ Toast.error({
content: 'Failed to copy nodes', content: 'Failed to copy nodes',
}); });
@ -86,16 +86,16 @@ export function shortcuts(shortcutsRegistry: ShortcutsRegistry, ctx: FreeLayoutP
if (selectedNodes && Array.isArray(selectedNodes)) { if (selectedNodes && Array.isArray(selectedNodes)) {
const newNodes = await Promise.all( const newNodes = await Promise.all(
selectedNodes.map(async node => { selectedNodes.map(async (node) => {
const nodeJSON = await document.toNodeJSON(node); const nodeJSON = await document.toNodeJSON(node);
return document.copyNodeFromJSON( return document.copyNodeFromJSON(
nodeJSON.type as string, nodeJSON.type as string,
nodeJSON, nodeJSON,
'', '',
nodeJSON.meta?.position, nodeJSON.meta?.position,
node.parent?.id, node.parent?.id
); );
}), })
); );
return newNodes; return newNodes;
} }
@ -137,7 +137,7 @@ export function shortcuts(shortcutsRegistry: ShortcutsRegistry, ctx: FreeLayoutP
? dragService.adjustSubNodePosition( ? dragService.adjustSubNodePosition(
nodeJSON.type as string, nodeJSON.type as string,
containerNode, containerNode,
nodeJSON.meta?.position, nodeJSON.meta?.position
) )
: nodeJSON.meta?.position; : nodeJSON.meta?.position;
return document.copyNodeFromJSON( return document.copyNodeFromJSON(
@ -145,9 +145,9 @@ export function shortcuts(shortcutsRegistry: ShortcutsRegistry, ctx: FreeLayoutP
nodeJSON, nodeJSON,
'', '',
position, position,
containerNode?.id, containerNode?.id
); );
}), })
); );
if (nodes.length > 0) { if (nodes.length > 0) {
@ -171,10 +171,10 @@ export function shortcuts(shortcutsRegistry: ShortcutsRegistry, ctx: FreeLayoutP
const selection = ctx.selection; const selection = ctx.selection;
const selectNodes = selection.selection.filter( const selectNodes = selection.selection.filter(
_entity => _entity instanceof FlowNodeEntity, (_entity) => _entity instanceof FlowNodeEntity
) as FlowNodeEntity[]; ) as FlowNodeEntity[];
selectNodes.forEach(node => { selectNodes.forEach((node) => {
node.renderData.expanded = false; node.renderData.expanded = false;
}); });
}, },
@ -191,10 +191,10 @@ export function shortcuts(shortcutsRegistry: ShortcutsRegistry, ctx: FreeLayoutP
const selection = ctx.selection; const selection = ctx.selection;
const selectNodes = selection.selection.filter( const selectNodes = selection.selection.filter(
_entity => _entity instanceof FlowNodeEntity, (_entity) => _entity instanceof FlowNodeEntity
) as FlowNodeEntity[]; ) as FlowNodeEntity[];
selectNodes.forEach(node => { selectNodes.forEach((node) => {
node.renderData.expanded = true; node.renderData.expanded = true;
}); });
}, },

View File

@ -7,4 +7,9 @@ module.exports = defineConfig({
'no-console': 'off', 'no-console': 'off',
'react/prop-types': 'off', 'react/prop-types': 'off',
}, },
settings: {
react: {
version: 'detect', // 自动检测 React 版本
},
},
}); });

View File

@ -251,6 +251,14 @@
"enableParallelism": true, "enableParallelism": true,
"safeForSimultaneousRushProcesses": true "safeForSimultaneousRushProcesses": true
}, },
{
"name": "lint:fix",
"commandKind": "bulk",
"summary": "⭐️️ Run eslint fix in packages",
"ignoreMissingScript": true,
"enableParallelism": true,
"safeForSimultaneousRushProcesses": true
},
{ {
"name": "dev:demo-fixed-layout", "name": "dev:demo-fixed-layout",
"commandKind": "global", "commandKind": "global",

View File

@ -524,6 +524,9 @@ importers:
'@types/node': '@types/node':
specifier: ^18 specifier: ^18
version: 18.19.68 version: 18.19.68
react:
specifier: ^18
version: 18.3.1
typescript: typescript:
specifier: ^5.0.4 specifier: ^5.0.4
version: 5.0.4 version: 5.0.4

View File

@ -7,4 +7,9 @@ const { defineConfig } = require(path.resolve(__dirname, main));
module.exports = defineConfig({ module.exports = defineConfig({
packageRoot: __dirname, packageRoot: __dirname,
preset: 'node', preset: 'node',
settings: {
react: {
version: 'detect', // 自动检测 React 版本
},
},
}); });

View File

@ -8,7 +8,6 @@
"build": "tsc -b --force", "build": "tsc -b --force",
"dev": "npm run build -- -w", "dev": "npm run build -- -w",
"lint": "eslint ./src --cache", "lint": "eslint ./src --cache",
"lint:fix": "eslint --fix ../../packages",
"test": "exit", "test": "exit",
"test:cov": "exit" "test:cov": "exit"
}, },
@ -38,6 +37,7 @@
"devDependencies": { "devDependencies": {
"@flowgram.ai/ts-config": "workspace:*", "@flowgram.ai/ts-config": "workspace:*",
"@types/node": "^18", "@types/node": "^18",
"react": "^18",
"typescript": "^5.0.4" "typescript": "^5.0.4"
}, },
"publishConfig": { "publishConfig": {