mirror of https://github.com/langgenius/dify.git
feat: expend toggle
This commit is contained in:
parent
cc4ca942c9
commit
6adb986167
|
|
@ -11,6 +11,7 @@ type Props = {
|
|||
onHeightChange: (height: number) => void
|
||||
children: JSX.Element
|
||||
footer?: JSX.Element
|
||||
hideResize?: boolean
|
||||
}
|
||||
|
||||
const PromptEditorHeightResizeWrap: FC<Props> = ({
|
||||
|
|
@ -20,6 +21,7 @@ const PromptEditorHeightResizeWrap: FC<Props> = ({
|
|||
onHeightChange,
|
||||
children,
|
||||
footer,
|
||||
hideResize,
|
||||
}) => {
|
||||
const [clientY, setClientY] = useState(0)
|
||||
const [isResizing, setIsResizing] = useState(false)
|
||||
|
|
@ -80,11 +82,13 @@ const PromptEditorHeightResizeWrap: FC<Props> = ({
|
|||
</div>
|
||||
{/* resize handler */}
|
||||
{footer}
|
||||
<div
|
||||
className='absolute bottom-0 left-0 w-full flex justify-center h-2 cursor-row-resize'
|
||||
onMouseDown={handleStartResize}>
|
||||
<div className='w-5 h-[3px] rounded-sm bg-gray-300'></div>
|
||||
</div>
|
||||
{!hideResize && (
|
||||
<div
|
||||
className='absolute bottom-0 left-0 w-full flex justify-center h-2 cursor-row-resize'
|
||||
onMouseDown={handleStartResize}>
|
||||
<div className='w-5 h-[3px] rounded-sm bg-gray-300'></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 14H10M10 14V20M10 14L3 21M20 10H14M14 10V4M14 10L21 3M20 14V16.8C20 17.9201 20 18.4802 19.782 18.908C19.5903 19.2843 19.2843 19.5903 18.908 19.782C18.4802 20 17.9201 20 16.8 20H14M10 4H7.2C6.0799 4 5.51984 4 5.09202 4.21799C4.71569 4.40973 4.40973 4.71569 4.21799 5.09202C4 5.51984 4 6.07989 4 7.2V10" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 498 B |
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M4 14H10M10 14V20M10 14L3 21M20 10H14M14 10V4M14 10L21 3M20 14V16.8C20 17.9201 20 18.4802 19.782 18.908C19.5903 19.2843 19.2843 19.5903 18.908 19.782C18.4802 20 17.9201 20 16.8 20H14M10 4H7.2C6.0799 4 5.51984 4 5.09202 4.21799C4.71569 4.40973 4.40973 4.71569 4.21799 5.09202C4 5.51984 4 6.07989 4 7.2V10",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "2",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Collapse04"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Collapse04.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'Collapse04'
|
||||
|
||||
export default Icon
|
||||
|
|
@ -5,6 +5,7 @@ export { default as ChevronDownDouble } from './ChevronDownDouble'
|
|||
export { default as ChevronDown } from './ChevronDown'
|
||||
export { default as ChevronRight } from './ChevronRight'
|
||||
export { default as ChevronSelectorVertical } from './ChevronSelectorVertical'
|
||||
export { default as Collapse04 } from './Collapse04'
|
||||
export { default as FlipBackward } from './FlipBackward'
|
||||
export { default as RefreshCcw01 } from './RefreshCcw01'
|
||||
export { default as RefreshCw05 } from './RefreshCw05'
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import { useEventEmitterContextContext } from '@/context/event-emitter'
|
|||
|
||||
export type PromptEditorProps = {
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
value?: string
|
||||
editable?: boolean
|
||||
onChange?: (text: string) => void
|
||||
|
|
@ -81,6 +82,7 @@ export type PromptEditorProps = {
|
|||
|
||||
const PromptEditor: FC<PromptEditorProps> = ({
|
||||
className,
|
||||
style,
|
||||
value,
|
||||
editable = true,
|
||||
onChange,
|
||||
|
|
@ -158,7 +160,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
|||
<LexicalComposer initialConfig={{ ...initialConfig, editable }}>
|
||||
<div className='relative'>
|
||||
<RichTextPlugin
|
||||
contentEditable={<ContentEditable className={`${className} outline-none text-sm text-gray-700 leading-6`} />}
|
||||
contentEditable={<ContentEditable className={`${className} outline-none text-sm text-gray-700 leading-6`} style={style || {}} />}
|
||||
placeholder={<Placeholder />}
|
||||
ErrorBoundary={LexicalErrorBoundary}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import cn from 'classnames'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import ToggleExpandBtn from '../toggle-expand-btn'
|
||||
import PromptEditorHeightResizeWrap from '@/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap'
|
||||
import PromptEditor from '@/app/components/base/prompt-editor'
|
||||
import { Clipboard, ClipboardCheck } from '@/app/components/base/icons/src/vender/line/files'
|
||||
import { Expand04 } from '@/app/components/base/icons/src/vender/solid/arrows'
|
||||
import s from '@/app/components/app/configuration/config-prompt/style.module.css'
|
||||
import { Trash03 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
|
|
@ -40,93 +40,101 @@ const Editor: FC<Props> = ({
|
|||
copy(value)
|
||||
setIsCopied(true)
|
||||
}, [value])
|
||||
const [isExpanded, setIsExpanded] = React.useState(false)
|
||||
const toggleExpand = useCallback(() => {
|
||||
setIsExpanded(!isExpanded)
|
||||
}, [isExpanded])
|
||||
|
||||
const [isFocus, {
|
||||
setTrue: setFocus,
|
||||
setFalse: setBlur,
|
||||
}] = useBoolean(false)
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const [isExpand, setIsExpand] = useState(false)
|
||||
const [wrapHeight, setWrapHeight] = useState(ref.current?.clientHeight)
|
||||
const editorExpandHeight = isExpand ? wrapHeight! - 56 : 0
|
||||
useEffect(() => {
|
||||
setWrapHeight(ref.current?.clientHeight)
|
||||
}, [isExpand])
|
||||
return (
|
||||
<div className={cn(isFocus && s.gradientBorder, '!rounded-[9px] shadow-md')}>
|
||||
<div className={cn(isFocus ? 'bg-white' : 'bg-gray-100', 'rounded-lg')}>
|
||||
<div className='pt-1 pl-3 pr-2 flex justify-between h-6 items-center'>
|
||||
<div className='leading-4 text-xs font-semibold text-gray-700 uppercase'>{title}</div>
|
||||
<div className='flex items-center'>
|
||||
<div className='leading-[18px] text-xs font-medium text-gray-500'>{value.length}</div>
|
||||
<div className='w-px h-3 ml-2 mr-2 bg-gray-200'></div>
|
||||
{/* Operations */}
|
||||
<div className='flex items-center space-x-2'>
|
||||
{showRemove && (
|
||||
<Trash03 className='w-3.5 h-3.5 text-gray-500 cursor-pointer' onClick={onRemove} />
|
||||
)}
|
||||
{!isCopied
|
||||
? (
|
||||
<Clipboard className='w-3.5 h-3.5 text-gray-500 cursor-pointer' onClick={handleCopy} />
|
||||
)
|
||||
: (
|
||||
<ClipboardCheck className='mx-1 w-3.5 h-3.5 text-gray-500' />
|
||||
)
|
||||
}
|
||||
<Expand04 className='w-3.5 h-3.5 text-gray-500 cursor-pointer' onClick={toggleExpand} />
|
||||
</div>
|
||||
<div className={cn(isExpand && 'absolute z-10 left-4 right-6 top-[52px] bottom-0 pb-4 bg-white')}>
|
||||
<div ref={ref} className={cn(isFocus && s.gradientBorder, isExpand && 'h-full', '!rounded-[9px] shadow-md')}>
|
||||
<div className={cn(isFocus ? 'bg-white' : 'bg-gray-100', isExpand && 'h-full flex flex-col', 'rounded-lg')}>
|
||||
<div className='pt-1 pl-3 pr-2 flex justify-between h-6 items-center'>
|
||||
<div className='leading-4 text-xs font-semibold text-gray-700 uppercase'>{title}</div>
|
||||
<div className='flex items-center'>
|
||||
<div className='leading-[18px] text-xs font-medium text-gray-500'>{value.length}</div>
|
||||
<div className='w-px h-3 ml-2 mr-2 bg-gray-200'></div>
|
||||
{/* Operations */}
|
||||
<div className='flex items-center space-x-2'>
|
||||
{showRemove && (
|
||||
<Trash03 className='w-3.5 h-3.5 text-gray-500 cursor-pointer' onClick={onRemove} />
|
||||
)}
|
||||
{!isCopied
|
||||
? (
|
||||
<Clipboard className='w-3.5 h-3.5 text-gray-500 cursor-pointer' onClick={handleCopy} />
|
||||
)
|
||||
: (
|
||||
<ClipboardCheck className='mx-1 w-3.5 h-3.5 text-gray-500' />
|
||||
)
|
||||
}
|
||||
<ToggleExpandBtn isExpand={isExpand} onExpandChange={setIsExpand} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<PromptEditorHeightResizeWrap
|
||||
className='px-3 min-h-[102px] overflow-y-auto text-sm text-gray-700'
|
||||
height={editorHeight}
|
||||
minHeight={minHeight}
|
||||
onHeightChange={setEditorHeight}
|
||||
footer={(
|
||||
<div className='pl-3 pb-2 flex'>
|
||||
{isFocus
|
||||
? <div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{'{x} '}{t('workflow.nodes.common.insertVarTip')}</div>
|
||||
: <div className='h-[18px]'></div>}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<PromptEditor
|
||||
className='min-h-[84px]'
|
||||
value={value}
|
||||
contextBlock={{
|
||||
show: true,
|
||||
selectable: true,
|
||||
datasets: [],
|
||||
onAddContext: () => { },
|
||||
}}
|
||||
variableBlock={{
|
||||
variables: variables.map(item => ({
|
||||
name: item,
|
||||
value: item,
|
||||
})),
|
||||
externalTools: [],
|
||||
onAddExternalTool: () => { },
|
||||
}}
|
||||
historyBlock={{
|
||||
show: true,
|
||||
selectable: true,
|
||||
history: {
|
||||
user: 'user',
|
||||
assistant: 'xxx',
|
||||
},
|
||||
onEditRole: () => { },
|
||||
}}
|
||||
queryBlock={{
|
||||
show: true,
|
||||
selectable: true,
|
||||
}}
|
||||
onChange={onChange}
|
||||
onBlur={setBlur}
|
||||
onFocus={setFocus}
|
||||
editable={!readOnly}
|
||||
/>
|
||||
</PromptEditorHeightResizeWrap>
|
||||
</div>
|
||||
<PromptEditorHeightResizeWrap
|
||||
className={cn(isExpand && 'h-0 grow', 'px-3 min-h-[102px] overflow-y-auto text-sm text-gray-700')}
|
||||
height={isExpand ? editorExpandHeight : editorHeight}
|
||||
minHeight={minHeight}
|
||||
onHeightChange={setEditorHeight}
|
||||
footer={(
|
||||
<div className='pl-3 pb-2 flex'>
|
||||
{isFocus
|
||||
? <div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{'{x} '}{t('workflow.nodes.common.insertVarTip')}</div>
|
||||
: <div className='h-[18px]'></div>}
|
||||
</div>
|
||||
)}
|
||||
hideResize={isExpand}
|
||||
>
|
||||
<PromptEditor
|
||||
className={cn('min-h-[84px]')}
|
||||
style={isExpand ? { height: editorExpandHeight - 5 } : {}}
|
||||
value={value}
|
||||
contextBlock={{
|
||||
show: true,
|
||||
selectable: true,
|
||||
datasets: [],
|
||||
onAddContext: () => { },
|
||||
}}
|
||||
variableBlock={{
|
||||
variables: variables.map(item => ({
|
||||
name: item,
|
||||
value: item,
|
||||
})),
|
||||
externalTools: [],
|
||||
onAddExternalTool: () => { },
|
||||
}}
|
||||
historyBlock={{
|
||||
show: true,
|
||||
selectable: true,
|
||||
history: {
|
||||
user: 'user',
|
||||
assistant: 'xxx',
|
||||
},
|
||||
onEditRole: () => { },
|
||||
}}
|
||||
queryBlock={{
|
||||
show: true,
|
||||
selectable: true,
|
||||
}}
|
||||
onChange={onChange}
|
||||
onBlur={setBlur}
|
||||
onFocus={setFocus}
|
||||
editable={!readOnly}
|
||||
/>
|
||||
</PromptEditorHeightResizeWrap>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
export default React.memo(Editor)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { Expand04 } from '@/app/components/base/icons/src/vender/solid/arrows'
|
||||
import { Collapse04 } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
|
||||
type Props = {
|
||||
isExpand: boolean
|
||||
onExpandChange: (isExpand: boolean) => void
|
||||
}
|
||||
|
||||
const ExpandBtn: FC<Props> = ({
|
||||
isExpand,
|
||||
onExpandChange,
|
||||
}) => {
|
||||
const handleToggle = useCallback(() => {
|
||||
onExpandChange(!isExpand)
|
||||
}, [isExpand])
|
||||
|
||||
const Icon = isExpand ? Collapse04 : Expand04
|
||||
return (
|
||||
<Icon className='w-3.5 h-3.5 text-gray-500 cursor-pointer' onClick={handleToggle} />
|
||||
)
|
||||
}
|
||||
export default React.memo(ExpandBtn)
|
||||
Loading…
Reference in New Issue