Skip to content

Commit

Permalink
✨ feat: add group
Browse files Browse the repository at this point in the history
  • Loading branch information
canisminor1990 committed Apr 7, 2023
1 parent f49397b commit a665a59
Show file tree
Hide file tree
Showing 21 changed files with 229 additions and 80 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- [ ] 拖拽图片自动上传并生成节点
- 编组
- [ ] 框选节点编组
- [ ] 编组相关基础功能
- [x] 编组相关基础功能
- [ ] 局部 Flow 转换为组件
- 节点
- [ ] 中继节点,支持一个到多个变量中继
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ImagePreview } from '@/components/NodeComponent/index'
import { ImagePreview } from '@/components/NodeComponent'
import { emptyImg } from '@/components/theme'
import { getBackendUrl } from '@/config'
import { Image } from 'antd'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { Position } from 'reactflow'
import { SpaceCol } from '../style'
import NodeSlot from './NodeSlot'
import { SpaceCol } from './style'

interface NodeInputsProps {
data: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { Position } from 'reactflow'
import { SpaceCol } from '../style'
import NodeSlot from './NodeSlot'
import { SpaceCol } from './style'

interface NodeOutpusProps {
data: string[]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { InputComponent } from '@/components'
import NodeSlot from '@/components/NodeComponent/NodeSlot'
import NodeSlot from '@/components/NodeComponent/SdNode/NodeSlot'
import { Flow } from '@/types'
import React from 'react'
import { Position } from 'reactflow'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { isArray, startCase } from 'lodash-es'
import React from 'react'
import { Handle, HandleType, Position } from 'reactflow'
import { shallow } from 'zustand/shallow'
import { Slot } from './style'
import { Slot } from '../style'

interface NodeSlotProps {
label: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { checkInput } from '@/utils'
import { startCase } from 'lodash-es'
import React from 'react'
import styled from 'styled-components'
import { NodeCard, SpaceCol, SpaceGrid } from './style'
import { NodeCard, SpaceCol, SpaceGrid } from '../style'

const Slot = styled.div<{ isRequired: 1 | 0 }>`
background: ${({ isRequired, theme }) => (isRequired ? theme.colorPrimary : theme.colorBorder)};
Expand Down
62 changes: 62 additions & 0 deletions src/components/NodeComponent/SdNode/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { SpaceGrid } from '@/components/NodeComponent/style'
import { useAppStore } from '@/store'
import { Widget } from '@/types'
import { checkInput } from '@/utils'
import React from 'react'
import { NodeProps } from 'reactflow'
import { shallow } from 'zustand/shallow'
import NodeImgPreview from './NodeImgPreview'
import NodeInputs from './NodeInputs'
import NodeOutpus from './NodeOutpus'
import NodeParams from './NodeParams'

const SdNode: React.FC<NodeProps<Widget>> = (node) => {
const { imagePreviews, inputImgPreviews } = useAppStore(
(st) => ({
imagePreviews: st.graph?.[node.id]?.images
?.map((image, index) => {
return {
image,
index,
}
})
.filter(Boolean),
inputImgPreviews: [
{
image: {
filename: st.onGetNodeFieldsData(node.id, 'image'),
type: 'input',
},
index: 0,
},
].filter((i) => i.image.filename),
onPropChange: st.onPropChange,
}),
shallow
)

const params: any[] = []
const inputs: any[] = []
const outputs: any[] = node.data.output

for (const [property, input] of Object.entries(node.data.input.required)) {
if (checkInput.isParameterOrList(input)) {
params.push({ name: property, type: input[0], input })
} else {
inputs.push({ name: property, type: input[0] })
}
}

return (
<>
<SpaceGrid>
<NodeInputs data={inputs} />
<NodeOutpus data={outputs} />
</SpaceGrid>
<NodeParams data={params} nodeId={node.id} />
<NodeImgPreview data={imagePreviews || inputImgPreviews} />
</>
)
}

export default React.memo(SdNode)
78 changes: 29 additions & 49 deletions src/components/NodeComponent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@ import { ActionIcon, Input } from '@/components'
import { ColorMenu, colorList } from '@/components/NodeComponent/ColorMenu'
import { useAppStore } from '@/store'
import { ImageItem, type Widget } from '@/types'
import { checkInput } from '@/utils'
import { CopyOutlined, DeleteOutlined, EditOutlined, HighlightOutlined, MoreOutlined } from '@ant-design/icons'
import { Dropdown, Progress, type MenuProps } from 'antd'
import { useTheme } from 'antd-style'
import { mix } from 'polished'
import React, { useState } from 'react'
import { mix, rgba } from 'polished'
import React, { useEffect, useRef, useState } from 'react'
import { NodeResizeControl, type NodeProps } from 'reactflow'
import { shallow } from 'zustand/shallow'
import NodeImgPreview from './NodeImgPreview'
import NodeInputs from './NodeInputs'
import NodeOutpus from './NodeOutpus'
import NodeParams from './NodeParams'
import { NodeCard, SpaceGrid } from './style'
import SdNode from './SdNode'
import { GroupCard, NodeCard } from './style'

export const NODE_IDENTIFIER = 'sdNode'

Expand All @@ -24,26 +20,10 @@ export interface ImagePreview {
}

const NodeComponent: React.FC<NodeProps<Widget>> = (node) => {
const { progressBar, imagePreviews, onDuplicateNode, onDeleteNode, onModifyChange, inputImgPreviews } = useAppStore(
const ref: any = useRef(null)
const { progressBar, onDuplicateNode, onDeleteNode, onModifyChange } = useAppStore(
(st) => ({
progressBar: st.nodeInProgress?.id === node.id ? st.nodeInProgress.progress : undefined,
imagePreviews: st.graph?.[node.id]?.images
?.map((image, index) => {
return {
image,
index,
}
})
.filter(Boolean),
inputImgPreviews: [
{
image: {
filename: st.onGetNodeFieldsData(node.id, 'image'),
type: 'input',
},
index: 0,
},
].filter((i) => i.image.filename),
onPropChange: st.onPropChange,
onDuplicateNode: st.onDuplicateNode,
onDeleteNode: st.onDeleteNode,
Expand All @@ -54,21 +34,10 @@ const NodeComponent: React.FC<NodeProps<Widget>> = (node) => {

const theme = useTheme()
const [nicknameInput, setNicknameInput] = useState<boolean>(false)
const params: any[] = []
const inputs: any[] = []
const outputs: any[] = node.data.output
const isInProgress = progressBar !== undefined
const isSelected = node.selected
const name = node.data?.nickname || node.data.name

for (const [property, input] of Object.entries(node.data.input.required)) {
if (checkInput.isParameterOrList(input)) {
params.push({ name: property, type: input[0], input })
} else {
inputs.push({ name: property, type: input[0] })
}
}

const isGroup = node.data.name === 'Group'
const handleNickname = (e: any) => {
const nickname = e.target.value
onModifyChange(node.id, 'nickname', nickname)
Expand Down Expand Up @@ -110,10 +79,26 @@ const NodeComponent: React.FC<NodeProps<Widget>> = (node) => {
},
]

const StyledCard = isGroup ? GroupCard : NodeCard
let background
if (isGroup) {
background = node.data?.color ? rgba(node.data.color, 0.2) : theme.colorFill
} else {
background = node.data?.color ? mix(0.8, theme.colorBgContainer, node.data.color) : theme.colorBgContainer
}

useEffect(() => {
if (isGroup) {
const parenet = ref.current?.parentNode
parenet.setAttribute('type', 'group')
}
}, [])

return (
<NodeCard
<StyledCard
ref={ref}
size="small"
style={node.data?.color ? { background: mix(0.8, theme.colorBgContainer, node.data.color) } : {}}
style={{ background }}
title={
nicknameInput ? (
<Input
Expand All @@ -129,7 +114,7 @@ const NodeComponent: React.FC<NodeProps<Widget>> = (node) => {
)
}
active={isInProgress || isSelected ? 1 : 0}
hoverable
hoverable={!isGroup}
extra={
isInProgress
? progressBar > 0 && <Progress steps={4} percent={Math.floor(progressBar * 100)} />
Expand All @@ -140,14 +125,9 @@ const NodeComponent: React.FC<NodeProps<Widget>> = (node) => {
)
}
>
<SpaceGrid>
<NodeInputs data={inputs} />
<NodeOutpus data={outputs} />
</SpaceGrid>
<NodeParams data={params} nodeId={node.id} />
<NodeImgPreview data={imagePreviews || inputImgPreviews} />
{isSelected && <NodeResizeControl />}
</NodeCard>
<SdNode {...node} />
{isSelected && <NodeResizeControl minWidth={80} minHeight={120} />}
</StyledCard>
)
}

Expand Down
29 changes: 27 additions & 2 deletions src/components/NodeComponent/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,42 @@ import { Position } from 'reactflow'
import styled, { css } from 'styled-components'

export const NodeCard = styled(Card)<{ active: 1 | 0 }>`
min-width: 240px;
box-shadow: ${({ theme }) => theme.boxShadowTertiary};
min-width: 80px;
min-height: 120px;
${({ active, theme }) =>
active
? css`
outline: 2px solid ${theme.colorPrimary};
`
: ''}
: ''};
.ant-card-head {
background: ${({ theme }) => theme.colorFillQuaternary} !important;
padding-right: 3px;
border-bottom: unset;
height: 15px;
}
`

export const GroupCard = styled(Card)<{ active: 1 | 0 }>`
min-width: 80px;
min-height: 120px;
height: 100%;
${({ active, theme }) =>
active
? css`
outline: 2px solid ${theme.colorPrimary};
`
: ''};
.react-flow__resize-control {
pointer-events: all !important;
}
.ant-card-head {
background: ${({ theme }) => theme.colorFillQuaternary} !important;
padding-right: 3px;
border-bottom: unset;
height: 15px;
pointer-events: all !important;
}
`

Expand Down
4 changes: 2 additions & 2 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export { default as WorkflowPageComponent } from './ControlPanelComponent/Workfl
export * from './EditorComponent'
export { default as Header } from './Header'
export { NODE_IDENTIFIER, default as NodeComponent } from './NodeComponent'
export { default as InputComponent } from './NodeComponent/InputComponent'
export { default as PreviewNode } from './NodeComponent/PreviewNode'
export { default as InputComponent } from './NodeComponent/SdNode/InputComponent'
export { default as PreviewNode } from './NodeComponent/SdNode/PreviewNode'
5 changes: 5 additions & 0 deletions src/layouts/GlobalStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ const GlobalStyle = createGlobalStyle`
font-family: 'Hack', 'IBM Plex Mono', 'ui-monospace', 'Consolas', monospace !important;
}
.react-flow__node[type="group"] {
z-index: -1 !important;
pointer-events: none !important;
}
.react-json-view {
background: transparent !important;
Expand Down
Loading

0 comments on commit a665a59

Please sign in to comment.