Commit 7fa25934 authored by Joel's avatar Joel

feat: key value input

parent 649c3d07
'use client'
import type { FC } from 'react'
import React from 'react'
import cn from 'classnames'
import { Trash03 } from '@/app/components/base/icons/src/vender/line/general'
type Props = {
className?: string
onClick: (e: React.MouseEvent) => void
}
const Remove: FC<Props> = ({
className,
onClick,
}) => {
return (
<div
className={cn(className, 'p-1 cursor-pointer rounded-md hover:bg-black/5 text-gray-500 hover:text-gray-800')}
onClick={onClick}
>
<Trash03 className='w-4 h-4' />
</div>
)
}
export default React.memo(Remove)
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import { useBoolean } from 'ahooks'
import type { KeyValue } from '../types'
import { Trash03 } from '@/app/components/base/icons/src/vender/line/general'
type Props = {
payload: KeyValue
onChange: (newPayload: KeyValue) => void
onRemove: () => void
isLastItem: boolean
onAdd: () => void
}
const KeyValueItem: FC<Props> = ({
payload,
onChange,
onRemove,
isLastItem,
onAdd,
}) => {
const [isKeyEditing, {
setTrue: setIsKeyEditing,
setFalse: setIsKeyEditingFalse,
}] = useBoolean(false)
const handleKeyChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
onChange({
key: e.target.value,
value: payload.value,
})
}, [])
return (
<div>
<div>
{isKeyEditing
? (
<input
type='text'
value={payload.key}
onChange={handleKeyChange}
onBlur={setIsKeyEditingFalse}
/>
)
: <div onClick={setIsKeyEditing}>{payload.key}</div>}
</div>
<div
>
{payload.value}
<div
className='p-1 cursor-pointer rounded-md hover:bg-black/5'
onClick={onRemove}
>
<Trash03 className='w-4 h-4' />
</div>
</div>
</div>
)
}
export default React.memo(KeyValueItem)
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import { useBoolean } from 'ahooks'
import cn from 'classnames'
import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button'
type Props = {
className?: string
value: string
onChange: (newValue: string) => void
hasRemove: boolean
onRemove?: () => void
}
const InputItem: FC<Props> = ({
className,
value,
onChange,
hasRemove,
onRemove,
}) => {
const [isEdit, {
setTrue: setIsEditTrue,
setFalse: setIsEditFalse,
}] = useBoolean(false)
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
onChange(e.target.value)
}, [onChange])
const handleRemove = useCallback((e: React.MouseEvent) => {
e.stopPropagation()
onRemove?.()
}, [onRemove])
return (
<div className={cn(className, !isEdit && 'hover:bg-gray-50 hover:cursor-text', 'relative flex h-full items-center pl-2')}>
{isEdit
? (
<input
type='text'
className='w-full h-[18px] leading-[18px] pl-0.5 text-gray-900 text-xs font-normal placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200'
value={value}
onChange={handleChange}
onBlur={setIsEditFalse}
autoFocus
/>
)
: <div
className="pl-0.5 w-full h-[18px] leading-[18px]"
onClick={setIsEditTrue}
>
<div className='text-gray-900 text-xs font-normal'>{value}</div>
{hasRemove && !isEdit && (
<RemoveButton
className='group-hover:block hidden absolute right-1 top-0.5'
onClick={handleRemove}
/>
)}
</div>}
</div>
)
}
export default React.memo(InputItem)
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import cn from 'classnames'
import produce from 'immer'
import type { KeyValue } from '../../types'
import InputItem from './input-item'
type Props = {
className?: string
readonly: boolean
canRemove: boolean
payload: KeyValue
onChange: (newPayload: KeyValue) => void
onRemove: () => void
isLastItem: boolean
onAdd: () => void
}
const KeyValueItem: FC<Props> = ({
className,
readonly,
canRemove,
payload,
onChange,
onRemove,
isLastItem,
onAdd,
}) => {
const handleChange = useCallback((key: string) => {
return (value: string) => {
const newPayload = produce(payload, (draft: any) => {
draft[key] = value
})
onChange(newPayload)
if (key === 'value' && isLastItem)
onAdd()
}
}, [onChange, onAdd, isLastItem, payload])
return (
// group class name is for hover row show remove button
<div className={cn(className, 'group flex items-center h-7 border-t border-gray-200')}>
<div className='w-1/2 h-full border-r border-gray-200'>
<InputItem
className='pr-2.5'
value={payload.key}
onChange={handleChange('key')}
hasRemove={false} />
</div>
<div className='w-1/2 h-full'>
<InputItem
className='pr-1'
value={payload.value}
onChange={handleChange('value')}
hasRemove={!readonly && canRemove}
onRemove={onRemove}
/>
</div>
</div>
)
}
export default React.memo(KeyValueItem)
'use client'
import type { FC } from 'react'
import React from 'react'
import type { KeyValue } from '../types'
import KeyValueItem from './key-value-item'
import React, { useCallback } from 'react'
import produce from 'immer'
import type { KeyValue } from '../../types'
import KeyValueItem from './item'
type Props = {
readonly: boolean
list: KeyValue[]
onChange: (newList: KeyValue[]) => void
onAdd: () => void
}
const KeyValueList: FC<Props> = ({
readonly,
list,
onChange,
onAdd,
}) => {
const handleChange = useCallback((index: number) => {
return (newItem: KeyValue) => {
const newList = produce(list, (draft: any) => {
draft[index] = newItem
})
onChange(newList)
}
}, [list, onChange])
const handleRemove = useCallback((index: number) => {
return () => {
const newList = produce(list, (draft: any) => {
draft.splice(index, 1)
})
onChange(newList)
}
}, [list, onChange])
return (
<div>
<div>
<div>key</div>
<div>value</div>
<div className='border border-gray-200 rounded-lg overflow-hidden'>
<div className='flex items-center h-7 leading-7 text-xs font-medium text-gray-500 uppercase'>
<div className='w-1/2 h-full pl-3 border-r border-gray-200'>key</div>
<div className='w-1/2 h-full pl-3'>value</div>
</div>
{
list.map((item, index) => (
<KeyValueItem
key={index}
payload={item}
onChange={(newItem) => {
const newList = [...list]
newList[index] = newItem
onChange(newList)
}}
onRemove={() => {
const newList = [...list]
newList.splice(index, 1)
onChange(newList)
}}
onChange={handleChange(index)}
onRemove={handleRemove(index)}
isLastItem={index === list.length - 1}
onAdd={onAdd}
readonly={readonly}
canRemove={list.length > 1}
/>
))
}
......
import { BlockEnum } from '../../types'
import { MethodEnum } from './types'
import type { HttpNodeType } from './types'
export const mockData: HttpNodeType = {
title: 'Test',
desc: 'Test',
type: 'Test',
type: BlockEnum.HttpRequest,
variables: [
{
variable: 'name',
......@@ -14,9 +16,9 @@ export const mockData: HttpNodeType = {
value_selector: ['bbb', 'b', 'c'],
},
],
method: 'get',
method: MethodEnum.get,
url: 'https://api.dify.com/xx',
headers: '',
headers: 'Content-Type: application/json\nAccept: */*',
params: '',
body: {
type: 'json',
......
......@@ -3,11 +3,13 @@ import { useTranslation } from 'react-i18next'
import useConfig from './use-config'
import { mockData } from './mock'
import ApiInput from './components/api-input'
import KeyValueList from './components/key-value/list'
import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import AddButton from '@/app/components/base/button/add-button'
import Split from '@/app/components/workflow/nodes/_base/components/split'
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
const i18nPrefix = 'workflow.nodes.http'
const Panel: FC = () => {
......@@ -20,6 +22,9 @@ const Panel: FC = () => {
handleAddVariable,
handleMethodChange,
handleUrlChange,
headers,
setHeaders,
addHeader,
} = useConfig(mockData)
return (
......@@ -51,7 +56,12 @@ const Panel: FC = () => {
<Field
title={t(`${i18nPrefix}.headers`)}
>
headers
<KeyValueList
list={headers}
onChange={setHeaders}
onAdd={addHeader}
readonly={readOnly}
/>
</Field>
<Field
title={t(`${i18nPrefix}.params`)}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment