feat: implement AutoWidthInput component for dynamic input sizing in visual editor

This commit is contained in:
twwu 2025-03-27 14:39:25 +08:00
parent d29b3292f0
commit e250f5aab2
4 changed files with 88 additions and 11 deletions

View File

@ -18,8 +18,8 @@ const Card: FC<CardProps> = ({
return ( return (
<div className='flex flex-col py-0.5'> <div className='flex flex-col py-0.5'>
<div className='flex items-center gap-x-1 p-0.5 pl-1'> <div className='flex h-6 items-center gap-x-1 pl-1 pr-0.5'>
<div className='system-sm-semibold truncate px-1 py-0.5 text-text-primary'> <div className='system-sm-semibold truncate border border-transparent px-1 py-px text-text-primary'>
{name} {name}
</div> </div>
<div className='system-xs-medium px-1 py-0.5 text-text-tertiary'> <div className='system-xs-medium px-1 py-0.5 text-text-tertiary'>

View File

@ -0,0 +1,81 @@
import React, { useEffect, useState } from 'react'
import type { FC } from 'react'
import cn from '@/utils/classnames'
type AutoWidthInputProps = {
value: string
placeholder: string
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
onBlur: () => void
minWidth?: number
maxWidth?: number
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'>
const AutoWidthInput: FC<AutoWidthInputProps> = ({
value,
placeholder,
onChange,
onBlur,
minWidth = 60,
maxWidth = 300,
className,
...props
}) => {
const [width, setWidth] = useState(minWidth)
const textRef = React.useRef<HTMLSpanElement>(null)
useEffect(() => {
if (textRef.current) {
textRef.current.textContent = value || placeholder
const textWidth = textRef.current.offsetWidth
const newWidth = Math.max(minWidth, Math.min(textWidth + 16, maxWidth))
if (width !== newWidth)
setWidth(newWidth)
}
}, [value, placeholder, minWidth, maxWidth, width])
// Handle Enter key
const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && e.currentTarget.blur)
e.currentTarget.blur()
if (props.onKeyUp)
props.onKeyUp(e)
}
return (
<div className='relative inline-flex items-center'>
{/* Hidden measurement span */}
<span
ref={textRef}
className='system-sm-semibold invisible absolute left-0 top-0 -z-10 whitespace-pre px-1'
aria-hidden="true"
>
{value || placeholder}
</span>
{/* Actual input element */}
<input
value={value}
className={cn(
'system-sm-semibold placeholder:system-sm-semibold h-5 rounded-[5px] border border-transparent px-1',
'py-px text-text-primary caret-[#295EFF] shadow-shadow-shadow-3 outline-none',
'placeholder:text-text-placeholder hover:bg-state-base-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs',
className,
)}
style={{
width: `${width}px`,
minWidth: `${minWidth}px`,
maxWidth: `${maxWidth}px`,
transition: 'width 100ms ease-out',
}}
placeholder={placeholder}
onChange={onChange}
onBlur={onBlur}
onKeyUp={handleKeyUp}
{...props}
/>
</div>
)
}
export default React.memo(AutoWidthInput)

View File

@ -14,6 +14,7 @@ import { useVisualEditorStore } from '../store'
import { useMittContext } from '../context' import { useMittContext } from '../context'
import { useUnmount } from 'ahooks' import { useUnmount } from 'ahooks'
import { JSON_SCHEMA_MAX_DEPTH } from '@/config' import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
import AutoWidthInput from './auto-width-input'
export type EditData = { export type EditData = {
name: string name: string
@ -192,17 +193,15 @@ const EditCard: FC<EditCardProps> = ({
return ( return (
<div className='flex flex-col rounded-lg bg-components-panel-bg py-0.5 shadow-sm shadow-shadow-shadow-4'> <div className='flex flex-col rounded-lg bg-components-panel-bg py-0.5 shadow-sm shadow-shadow-shadow-4'>
<div className='flex items-center pl-1 pr-0.5'> <div className='flex h-6 items-center pl-1 pr-0.5'>
<div className='flex grow items-center gap-x-1'> <div className='flex grow items-center gap-x-1'>
<input <AutoWidthInput
value={currentFields.name} value={currentFields.name}
className='system-sm-semibold placeholder:system-sm-semibold h-5 max-w-20 rounded-[5px] border border-transparent px-1
py-0.5 text-text-primary caret-[#295EFF] shadow-shadow-shadow-3 outline-none
placeholder:text-text-placeholder hover:bg-state-base-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs'
placeholder={t('workflow.nodes.llm.jsonSchema.fieldNamePlaceholder')} placeholder={t('workflow.nodes.llm.jsonSchema.fieldNamePlaceholder')}
minWidth={80}
maxWidth={300}
onChange={handlePropertyNameChange} onChange={handlePropertyNameChange}
onBlur={handlePropertyNameBlur} onBlur={handlePropertyNameBlur}
onKeyUp={e => e.key === 'Enter' && e.currentTarget.blur()}
/> />
<TypeSelector <TypeSelector
currentValue={currentFields.type} currentValue={currentFields.type}

View File

@ -48,9 +48,6 @@ export default function Page() {
], ],
}, },
}, },
completed: {
type: Type.boolean,
},
}, },
required: [ required: [
'userId', 'userId',