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',
'react/prop-types': 'off',
},
settings: {
react: {
version: 'detect', // 自动检测 React 版本
},
},
});

View File

@ -1,7 +1,6 @@
import { FlowMinimapService, MinimapRender } from '@flowgram.ai/minimap-plugin';
import { useService } from '@flowgram.ai/fixed-layout-editor';
export const Minimap = () => {
const minimapService = useService(FlowMinimapService);
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';
export function Tools() {
@ -15,13 +16,21 @@ export function Tools() {
return () => disposable.dispose();
}, [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.zoomout()}>ZoomOut</button>
<button onClick={() => tools.fitView()}>Fitview</button>
<button onClick={() => tools.changeLayout()}>ChangeLayout</button>
<button onClick={() => history.undo()} disabled={!canUndo}>Undo</button>
<button onClick={() => history.redo()} disabled={!canRedo}>Redo</button>
<button onClick={() => history.undo()} disabled={!canUndo}>
Undo
</button>
<button onClick={() => history.redo()} disabled={!canRedo}>
Redo
</button>
<span>{Math.floor(tools.zoom * 100)}%</span>
</div>
);
}

View File

@ -1,13 +1,13 @@
import { FixedLayoutEditorProvider, EditorRenderer } from '@flowgram.ai/fixed-layout-editor';
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 { initialData } from './initial-data'
import { nodeRegistries } from './node-registries'
import { Tools } from './components/tools'
import { Minimap } from './components/minimap'
import { Tools } from './components/tools';
import { Minimap } from './components/minimap';
export const Editor = () => {
const editorProps = useEditorProps(initialData, nodeRegistries);

View File

@ -11,7 +11,7 @@ export const initialData: FlowDocumentJSON = {
type: 'start',
data: {
title: 'Start',
content: 'start content'
content: 'start content',
},
blocks: [],
},
@ -20,7 +20,7 @@ export const initialData: FlowDocumentJSON = {
id: 'condition_0',
type: 'condition',
data: {
title: 'Condition'
title: 'Condition',
},
blocks: [
{
@ -28,7 +28,7 @@ export const initialData: FlowDocumentJSON = {
type: 'block',
data: {
title: 'Branch 0',
content: 'branch 1 content'
content: 'branch 1 content',
},
blocks: [
{
@ -36,7 +36,7 @@ export const initialData: FlowDocumentJSON = {
type: 'custom',
data: {
title: 'Custom',
content: 'custrom content'
content: 'custrom content',
},
},
],
@ -46,7 +46,7 @@ export const initialData: FlowDocumentJSON = {
type: 'block',
data: {
title: 'Branch 1',
content: 'branch 1 content'
content: 'branch 1 content',
},
blocks: [],
},
@ -58,9 +58,8 @@ export const initialData: FlowDocumentJSON = {
type: 'end',
data: {
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 { FlowNodeRegistry } from '@flowgram.ai/fixed-layout-editor';
/**
*
@ -67,9 +67,9 @@ export const nodeRegistries: FlowNodeRegistry[] = [
type: 'custom',
data: {
title: 'Custom',
content: 'this is custom content'
}
}
}
}
content: 'this is custom content',
},
};
},
},
];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@ const Warning = styled.span`
export const Feedback = ({ errors, warnings }: StatePanelProps) => {
const renderFeedbacks = (fs: FieldError[] | FieldWarning[] | undefined) => {
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 (
<div>

View File

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

View File

@ -18,7 +18,7 @@ export function FormInputs() {
if (!properties) {
return <></>;
}
const content = Object.keys(properties).map(key => {
const content = Object.keys(properties).map((key) => {
const property = properties[key];
return (
<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>}
</div>
),
[],
[]
);
return (
<div

View File

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

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@ interface Props {
[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 [data, setData] = useState(value);
@ -46,7 +46,7 @@ const BaseTextarea: React.FC<Props> = props => {
{...rest}
ref={textareaRef}
value={data}
onChange={v => {
onChange={(v) => {
setData(v);
}}
onEnterPress={onSubmit}

View File

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

View File

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

View File

@ -7,4 +7,9 @@ module.exports = defineConfig({
'no-console': '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 { useService } from '@flowgram.ai/free-layout-editor';
export const Minimap = () => {
const minimapService = useService(FlowMinimapService);
return (

View File

@ -4,21 +4,23 @@ import { WorkflowDragService, useService } from '@flowgram.ai/free-layout-editor
const cardkeys = ['Node1', 'Node2'];
export const NodeAddPanel: React.FC = props => {
export const NodeAddPanel: React.FC = (props) => {
const startDragSerivce = useService<WorkflowDragService>(WorkflowDragService);
return (
<div className="demo-free-sidebar">
{cardkeys.map(nodeType => (
{cardkeys.map((nodeType) => (
<div
key={nodeType}
className="demo-free-card"
onMouseDown={e => startDragSerivce.startDragCard(nodeType, e, {
onMouseDown={(e) =>
startDragSerivce.startDragCard(nodeType, e, {
data: {
title: `New ${nodeType}`,
content: 'xxxx'
content: 'xxxx',
},
})
}
})}
>
{nodeType}
</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';
export function Tools() {
@ -15,13 +16,21 @@ export function Tools() {
return () => disposable.dispose();
}, [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.zoomout()}>ZoomOut</button>
<button onClick={() => tools.fitView()}>Fitview</button>
<button onClick={() => tools.autoLayout()}>AutoLayout</button>
<button onClick={() => history.undo()} disabled={!canUndo}>Undo</button>
<button onClick={() => history.redo()} disabled={!canRedo}>Redo</button>
<button onClick={() => history.undo()} disabled={!canUndo}>
Undo
</button>
<button onClick={() => history.redo()} disabled={!canRedo}>
Redo
</button>
<span>{Math.floor(tools.zoom * 100)}%</span>
</div>
);
}

View File

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

View File

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

View File

@ -10,7 +10,7 @@ export const initialData: WorkflowJSON = {
},
data: {
title: 'Start',
content: 'Start content'
content: 'Start content',
},
},
{
@ -21,7 +21,7 @@ export const initialData: WorkflowJSON = {
},
data: {
title: 'Custom',
content: 'Custom node content'
content: 'Custom node content',
},
},
{
@ -32,7 +32,7 @@ export const initialData: WorkflowJSON = {
},
data: {
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',
meta: {
},
meta: {},
defaultPorts: [{ type: 'output' }, { type: 'input' }], // A normal node has two ports
},
];

View File

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

View File

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

View File

@ -4,6 +4,9 @@ import {
usePlaygroundTools,
type InteractiveType as IdeInteractiveType,
} 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 SHOW_KEY = 'show_workflow_interactive_type_guide';
@ -25,9 +28,6 @@ export enum InteractiveType {
Mouse = 'MOUSE',
Pad = 'PAD',
}
import { Tooltip, Popover } from '@douyinfe/semi-ui';
import { MousePadSelector } from './mouse-pad-selector';
export const Interactive = () => {
const tools = usePlaygroundTools();

View File

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

View File

@ -20,7 +20,7 @@ const Warning = styled.span`
export const Feedback = ({ errors, warnings, invalid }: StatePanelProps) => {
const renderFeedbacks = (fs: FieldError[] | FieldWarning[] | undefined) => {
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 (
<div>

View File

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

View File

@ -18,7 +18,7 @@ export function FormInputs() {
if (!properties) {
return <></>;
}
const content = Object.keys(properties).map(key => {
const content = Object.keys(properties).map((key) => {
const property = properties[key];
return (
<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>}
</div>
),
[],
[]
);
return (
<div

View File

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

View File

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

View File

@ -29,7 +29,7 @@ export function ConditionInputs() {
<FormItem name="if" type="boolean" required={true} labelWidth={40}>
<FxExpression
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={
<Button
theme="borderless"

View File

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

View File

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

View File

@ -251,6 +251,14 @@
"enableParallelism": 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",
"commandKind": "global",

View File

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

View File

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

View File

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