diff --git a/web/app/components/base/form/components/field/select.tsx b/web/app/components/base/form/components/field/select.tsx index d13babf825..60f9f21943 100644 --- a/web/app/components/base/form/components/field/select.tsx +++ b/web/app/components/base/form/components/field/select.tsx @@ -1,5 +1,6 @@ import cn from '@/utils/classnames' import { useFieldContext } from '../..' +import type { PureSelectProps } from '../../../select/pure' import PureSelect from '../../../select/pure' import Label from '../label' import { useCallback } from 'react' @@ -18,7 +19,7 @@ type SelectFieldProps = { tooltip?: string className?: string labelClassName?: string -} +} & Omit const SelectField = ({ label, @@ -29,6 +30,7 @@ const SelectField = ({ tooltip, className, labelClassName, + ...selectProps }: SelectFieldProps) => { const field = useFieldContext() @@ -51,6 +53,7 @@ const SelectField = ({ value={field.state.value} options={options} onChange={handleChange} + {...selectProps} /> ) diff --git a/web/app/components/base/form/form-scenarios/input-field/index.tsx b/web/app/components/base/form/form-scenarios/input-field/index.tsx index 2468238044..eb21eb0ec6 100644 --- a/web/app/components/base/form/form-scenarios/input-field/index.tsx +++ b/web/app/components/base/form/form-scenarios/input-field/index.tsx @@ -103,6 +103,9 @@ const InputFieldForm = ({ label={t('appDebug.variableConfig.fieldType')} options={inputTypes} onChange={handleTypeChange} + popupProps={{ + wrapperClassName: 'z-40', + }} /> )} /> diff --git a/web/app/components/base/select/pure.tsx b/web/app/components/base/select/pure.tsx index 81cc2fbadf..05d0e864fc 100644 --- a/web/app/components/base/select/pure.tsx +++ b/web/app/components/base/select/pure.tsx @@ -22,7 +22,7 @@ type Option = { value: string } -type PureSelectProps = { +export type PureSelectProps = { options: Option[] value?: string onChange?: (value: string) => void diff --git a/web/app/components/rag-pipeline/components/input-field/dialog-wrapper.tsx b/web/app/components/rag-pipeline/components/input-field/dialog-wrapper.tsx new file mode 100644 index 0000000000..aa118f8840 --- /dev/null +++ b/web/app/components/rag-pipeline/components/input-field/dialog-wrapper.tsx @@ -0,0 +1,55 @@ +import { Fragment, useCallback } from 'react' +import type { ReactNode } from 'react' +import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react' +import cn from '@/utils/classnames' + +type DialogWrapperProps = { + className?: string + panelWrapperClassName?: string + children: ReactNode + show: boolean + onClose?: () => void +} + +const DialogWrapper = ({ + className, + panelWrapperClassName, + children, + show, + onClose, +}: DialogWrapperProps) => { + const close = useCallback(() => onClose?.(), [onClose]) + return ( + + + +
+ + +
+
+ + + {children} + + +
+
+
+
+ ) +} + +export default DialogWrapper diff --git a/web/app/components/rag-pipeline/components/input-field/editor.tsx b/web/app/components/rag-pipeline/components/input-field/editor.tsx new file mode 100644 index 0000000000..90b33d55a4 --- /dev/null +++ b/web/app/components/rag-pipeline/components/input-field/editor.tsx @@ -0,0 +1,49 @@ +import InputFieldForm from '@/app/components/base/form/form-scenarios/input-field' +import { RiCloseLine } from '@remixicon/react' +import DialogWrapper from './dialog-wrapper' +import type { InputVar } from '@/app/components/workflow/types' + +type InputFieldEditorProps = { + show: boolean + onClose: () => void + initialData?: InputVar +} + +const InputFieldEditor = ({ + show, + onClose, + initialData, +}: InputFieldEditorProps) => { + return ( + +
+
+ Add Input Field +
+ + { + console.log('submit', value) + onClose() + }} + /> +
+
+ ) +} + +export default InputFieldEditor diff --git a/web/app/components/rag-pipeline/components/panel/input-field/field-list/field-item.tsx b/web/app/components/rag-pipeline/components/input-field/field-list/field-item.tsx similarity index 84% rename from web/app/components/rag-pipeline/components/panel/input-field/field-list/field-item.tsx rename to web/app/components/rag-pipeline/components/input-field/field-list/field-item.tsx index a3e6f3f9f1..36ef082b10 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/field-list/field-item.tsx +++ b/web/app/components/rag-pipeline/components/input-field/field-list/field-item.tsx @@ -1,14 +1,13 @@ 'use client' -import React, { useCallback, useRef } from 'react' +import React, { useRef } from 'react' import { useHover } from 'ahooks' import { useTranslation } from 'react-i18next' import { RiDeleteBinLine, + RiDraggable, RiEditLine, } from '@remixicon/react' import type { InputVar } from '@/app/components/workflow/types' -import { noop } from 'lodash-es' -import { useStore } from '@/app/components/workflow/store' import { InputField } from '@/app/components/base/icons/src/public/pipeline' import InputVarTypeIcon from '@/app/components/workflow/nodes/_base/components/input-var-type-icon' import cn from '@/utils/classnames' @@ -17,23 +16,20 @@ import Badge from '@/app/components/base/badge' type FieldItemProps = { readonly?: boolean payload: InputVar - onRemove?: () => void + onClickEdit: () => void + onRemove: () => void } const FieldItem = ({ readonly, payload, - onRemove = noop, + onClickEdit, + onRemove, }: FieldItemProps) => { const { t } = useTranslation() const ref = useRef(null) const isHovering = useHover(ref) - const setShowInputFieldEditor = useStore(state => state.setShowInputFieldEditor) - - const showInputFieldEditor = useCallback(() => { - setShowInputFieldEditor?.(true) - }, [setShowInputFieldEditor]) return (
- + { + isHovering + ? + : + }
diff --git a/web/app/components/rag-pipeline/components/panel/input-field/field-list/index.tsx b/web/app/components/rag-pipeline/components/input-field/field-list/index.tsx similarity index 65% rename from web/app/components/rag-pipeline/components/panel/input-field/field-list/index.tsx rename to web/app/components/rag-pipeline/components/input-field/field-list/index.tsx index afac5b91bd..fb2e61caf9 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/field-list/index.tsx +++ b/web/app/components/rag-pipeline/components/input-field/field-list/index.tsx @@ -1,8 +1,9 @@ -import { useStore } from '@/app/components/workflow/store' import type { InputVar } from '@/app/components/workflow/types' import { RiAddLine } from '@remixicon/react' import FieldItem from './field-item' import cn from '@/utils/classnames' +import { useState } from 'react' +import InputFieldEditor from '../editor' type FieldListProps = { LabelRightContent: React.ReactNode @@ -17,13 +18,18 @@ const FieldList = ({ readonly, labelClassName, }: FieldListProps) => { - const showInputFieldEditor = useStore(state => state.showInputFieldEditor) - const setShowInputFieldEditor = useStore(state => state.setShowInputFieldEditor) - - const isReadonly = readonly || showInputFieldEditor + const [showInputFieldEditor, setShowInputFieldEditor] = useState(false) const handleAddField = () => { - setShowInputFieldEditor?.(true) + setShowInputFieldEditor(true) + } + + const handleEditField = (index: number) => { + setShowInputFieldEditor(true) + } + + const handleCloseEditor = () => { + setShowInputFieldEditor(false) } return ( @@ -36,8 +42,8 @@ const FieldList = ({ type='button' className='h-6 px-2 py-1 disabled:cursor-not-allowed' onClick={handleAddField} - disabled={isReadonly} - aria-disabled={isReadonly} + disabled={readonly} + aria-disabled={readonly} > @@ -46,14 +52,21 @@ const FieldList = ({ {inputFields?.map((item, index) => ( { // Handle remove action }} + onClickEdit={handleEditField.bind(null, index)} /> ))}
+ {showInputFieldEditor && ( + + )}
) } diff --git a/web/app/components/rag-pipeline/components/panel/input-field/footer-tip.tsx b/web/app/components/rag-pipeline/components/input-field/footer-tip.tsx similarity index 100% rename from web/app/components/rag-pipeline/components/panel/input-field/footer-tip.tsx rename to web/app/components/rag-pipeline/components/input-field/footer-tip.tsx diff --git a/web/app/components/rag-pipeline/components/panel/input-field/index.tsx b/web/app/components/rag-pipeline/components/input-field/index.tsx similarity index 84% rename from web/app/components/rag-pipeline/components/panel/input-field/index.tsx rename to web/app/components/rag-pipeline/components/input-field/index.tsx index d456c87500..3a062f8163 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/index.tsx +++ b/web/app/components/rag-pipeline/components/input-field/index.tsx @@ -4,31 +4,35 @@ import { } from 'react' import { useStore } from '@/app/components/workflow/store' import { RiCloseLine } from '@remixicon/react' -import FieldList from './field-list' import { Jina } from '@/app/components/base/icons/src/public/llm' import { InputVarType } from '@/app/components/workflow/types' import Tooltip from '@/app/components/base/tooltip' +import DialogWrapper from './dialog-wrapper' +import FieldList from './field-list' import FooterTip from './footer-tip' -import InputFieldEditor from './editor' -type InputFieldPanelProps = { +type InputFieldDialogProps = { readonly?: boolean } -const InputFieldPanel = ({ +const InputFieldDialog = ({ readonly = false, -}: InputFieldPanelProps) => { - const showInputFieldEditor = useStore(state => state.showInputFieldEditor) - const setShowInputFieldPanel = useStore(state => state.setShowInputFieldPanel) +}: InputFieldDialogProps) => { + const showInputFieldDialog = useStore(state => state.showInputFieldDialog) + const setShowInputFieldDialog = useStore(state => state.setShowInputFieldDialog) const closePanel = useCallback(() => { - setShowInputFieldPanel?.(false) - }, [setShowInputFieldPanel]) + setShowInputFieldDialog?.(false) + }, [setShowInputFieldDialog]) return ( -
-
+ +
+ {/* // TODO: i18n */}
User input fields
@@ -60,6 +64,11 @@ const InputFieldPanel = ({ type: InputVarType.textInput, required: true, max_length: 12, + }, { + variable: 'num', + label: 'num', + type: InputVarType.number, + required: true, }]} readonly={readonly} labelClassName='pt-2 pb-1' @@ -108,9 +117,8 @@ const InputFieldPanel = ({
- {showInputFieldEditor && } -
+ ) } -export default memo(InputFieldPanel) +export default memo(InputFieldDialog) diff --git a/web/app/components/rag-pipeline/components/panel/index.tsx b/web/app/components/rag-pipeline/components/panel/index.tsx index aa9f69fc52..faa15a79f2 100644 --- a/web/app/components/rag-pipeline/components/panel/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/index.tsx @@ -1,19 +1,10 @@ -import { useStore } from '@/app/components/workflow/store' -import InputField from './input-field' import { useMemo } from 'react' import type { PanelProps } from '@/app/components/workflow/panel' import Panel from '@/app/components/workflow/panel' const RagPipelinePanelOnRight = () => { - const showInputField = useStore(s => s.showInputFieldPanel) - return ( <> - { - showInputField && ( - - ) - } ) } diff --git a/web/app/components/rag-pipeline/components/panel/input-field/editor.tsx b/web/app/components/rag-pipeline/components/panel/input-field/editor.tsx deleted file mode 100644 index a5e052d325..0000000000 --- a/web/app/components/rag-pipeline/components/panel/input-field/editor.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useStore } from '@/app/components/workflow/store' -import InputFieldForm from '@/app/components/base/form/form-scenarios/input-field' -import { useCallback } from 'react' -import { RiCloseLine } from '@remixicon/react' - -const InputFieldEditor = () => { - const setShowInputFieldEditor = useStore(state => state.setShowInputFieldEditor) - - const closeEditor = useCallback(() => { - setShowInputFieldEditor?.(false) - }, [setShowInputFieldEditor]) - - return ( -
-
- Add Input Field -
- - { - console.log('submit', value) - closeEditor() - }} - /> -
- ) -} - -export default InputFieldEditor diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx new file mode 100644 index 0000000000..9b4bf1ffd2 --- /dev/null +++ b/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx @@ -0,0 +1,20 @@ +import { useStore } from '../../workflow/store' +import InputField from './input-field' +import RagPipelinePanel from './panel' +import RagPipelineHeader from './rag-pipeline-header' + +const RagPipelineChildren = () => { + const showInputFieldDialog = useStore(state => state.showInputFieldDialog) + + return ( + <> + + + { + showInputFieldDialog && () + } + + ) +} + +export default RagPipelineChildren diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-header/input-field-button.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-header/input-field-button.tsx index 5fb50fe72a..6488f864da 100644 --- a/web/app/components/rag-pipeline/components/rag-pipeline-header/input-field-button.tsx +++ b/web/app/components/rag-pipeline/components/rag-pipeline-header/input-field-button.tsx @@ -3,12 +3,11 @@ import { InputField } from '@/app/components/base/icons/src/public/pipeline' import { useStore } from '@/app/components/workflow/store' import { useCallback } from 'react' -// TODO: i18n const InputFieldButton = () => { - const setShowInputFieldPanel = useStore(state => state.setShowInputFieldPanel) + const setShowInputFieldDialog = useStore(state => state.setShowInputFieldDialog) const handleClick = useCallback(() => { - setShowInputFieldPanel?.(true) - }, [setShowInputFieldPanel]) + setShowInputFieldDialog?.(true) + }, [setShowInputFieldDialog]) return ( ) diff --git a/web/app/components/rag-pipeline/index.tsx b/web/app/components/rag-pipeline/index.tsx index 4aa0cd85fb..228c96c4cb 100644 --- a/web/app/components/rag-pipeline/index.tsx +++ b/web/app/components/rag-pipeline/index.tsx @@ -1,13 +1,12 @@ import WorkflowWithDefaultContext, { WorkflowWithInnerContext, } from '@/app/components/workflow' -import RagPipelinePanel from './components/panel' import { WorkflowContextProvider, } from '@/app/components/workflow/context' import type { InjectWorkflowStoreSliceFn } from '@/app/components/workflow/store' -import RagPipelineHeader from './components/rag-pipeline-header' import { createRagPipelineSliceSlice } from './store' +import RagPipelineChildren from './components/rag-pipeline-children' const RagPipeline = () => { return ( @@ -22,8 +21,7 @@ const RagPipeline = () => { nodes={[]} edges={[]} > - - + diff --git a/web/app/components/rag-pipeline/store/index.ts b/web/app/components/rag-pipeline/store/index.ts index 5c5a4978af..146ecb4542 100644 --- a/web/app/components/rag-pipeline/store/index.ts +++ b/web/app/components/rag-pipeline/store/index.ts @@ -1,20 +1,16 @@ import type { StateCreator } from 'zustand' export type RagPipelineSliceShape = { - showInputFieldEditor: boolean - setShowInputFieldEditor: (showInputFieldDialog: boolean) => void - showInputFieldPanel: boolean - setShowInputFieldPanel: (showInputFieldPanel: boolean) => void + showInputFieldDialog: boolean + setShowInputFieldDialog: (showInputFieldPanel: boolean) => void nodesDefaultConfigs: Record setNodesDefaultConfigs: (nodesDefaultConfigs: Record) => void } export type CreateRagPipelineSliceSlice = StateCreator export const createRagPipelineSliceSlice: StateCreator = set => ({ - showInputFieldEditor: false, - setShowInputFieldEditor: showInputFieldEditor => set(() => ({ showInputFieldEditor })), - showInputFieldPanel: false, - setShowInputFieldPanel: showInputFieldPanel => set(() => ({ showInputFieldPanel })), + showInputFieldDialog: false, + setShowInputFieldDialog: showInputFieldDialog => set(() => ({ showInputFieldDialog })), nodesDefaultConfigs: {}, setNodesDefaultConfigs: nodesDefaultConfigs => set(() => ({ nodesDefaultConfigs })), })