feat: refine channel management UI
This commit is contained in:
+20
-19
@@ -43,6 +43,23 @@ type EditorRow = {
|
||||
value: string
|
||||
}
|
||||
|
||||
function parseJsonRows(json: string): EditorRow[] {
|
||||
try {
|
||||
if (!json.trim()) return []
|
||||
const parsed = JSON.parse(json)
|
||||
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
||||
return []
|
||||
}
|
||||
return Object.entries(parsed).map(([key, val], index) => ({
|
||||
id: `${Date.now()}-${index}`,
|
||||
key,
|
||||
value: typeof val === 'object' ? JSON.stringify(val) : String(val),
|
||||
}))
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export function JsonEditor({
|
||||
value,
|
||||
onChange,
|
||||
@@ -63,27 +80,11 @@ export function JsonEditor({
|
||||
const resolvedKeyLabel = keyLabel ?? t('Key')
|
||||
const resolvedValueLabel = valueLabel ?? t('Value')
|
||||
const [mode, setMode] = useState<'visual' | 'json'>('visual')
|
||||
const [rows, setRows] = useState<EditorRow[]>([])
|
||||
const [rows, setRows] = useState<EditorRow[]>(() => parseJsonRows(value))
|
||||
const [jsonValue, setJsonValue] = useState(value)
|
||||
|
||||
const parseJsonToRows = (json: string) => {
|
||||
try {
|
||||
if (!json.trim()) {
|
||||
setRows([])
|
||||
return
|
||||
}
|
||||
const parsed = JSON.parse(json)
|
||||
const newRows: EditorRow[] = Object.entries(parsed).map(
|
||||
([key, val], index) => ({
|
||||
id: `${Date.now()}-${index}`,
|
||||
key,
|
||||
value: typeof val === 'object' ? JSON.stringify(val) : String(val),
|
||||
})
|
||||
)
|
||||
setRows(newRows)
|
||||
} catch (_error) {
|
||||
// Invalid JSON, keep current rows
|
||||
}
|
||||
setRows(parseJsonRows(json))
|
||||
}
|
||||
|
||||
// Parse JSON to rows when value changes externally
|
||||
@@ -228,7 +229,7 @@ export function JsonEditor({
|
||||
<div className='grid grid-cols-[1fr_1fr_auto] gap-2 text-sm font-medium'>
|
||||
<div>{resolvedKeyLabel}</div>
|
||||
<div>{resolvedValueLabel}</div>
|
||||
<div className='w-10'></div>
|
||||
<div className='w-10' />
|
||||
</div>
|
||||
{rows.map((row) => (
|
||||
<div
|
||||
|
||||
+13
-1
@@ -37,6 +37,7 @@ interface ComboboxInputProps {
|
||||
className?: string
|
||||
id?: string
|
||||
allowCustomValue?: boolean
|
||||
openOnFocus?: boolean
|
||||
}
|
||||
|
||||
export function ComboboxInput({
|
||||
@@ -48,6 +49,7 @@ export function ComboboxInput({
|
||||
className,
|
||||
id,
|
||||
allowCustomValue = false,
|
||||
openOnFocus = true,
|
||||
}: ComboboxInputProps) {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = React.useState(false)
|
||||
@@ -56,6 +58,7 @@ export function ComboboxInput({
|
||||
const containerRef = React.useRef<HTMLDivElement>(null)
|
||||
const inputRef = React.useRef<HTMLInputElement>(null)
|
||||
const listRef = React.useRef<HTMLUListElement>(null)
|
||||
const pointerFocusRef = React.useRef(false)
|
||||
const selectedOption = React.useMemo(
|
||||
() => options.find((option) => option.value === value),
|
||||
[options, value]
|
||||
@@ -175,9 +178,18 @@ export function ComboboxInput({
|
||||
}
|
||||
if (!open) setOpen(true)
|
||||
}}
|
||||
onPointerDown={() => {
|
||||
pointerFocusRef.current = true
|
||||
if (document.activeElement === inputRef.current && !open) {
|
||||
setOpen(true)
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
setSearchValue(allowCustomValue && !selectedOption ? value : '')
|
||||
setOpen(true)
|
||||
if (openOnFocus || pointerFocusRef.current) {
|
||||
setOpen(true)
|
||||
}
|
||||
pointerFocusRef.current = false
|
||||
}}
|
||||
onKeyDown={handleKeyDown}
|
||||
className={cn('pr-9', className)}
|
||||
|
||||
+2
@@ -47,6 +47,7 @@ type LegacyComboboxProps = {
|
||||
allowCustomValue?: boolean
|
||||
className?: string
|
||||
id?: string
|
||||
openOnFocus?: boolean
|
||||
}
|
||||
|
||||
function Combobox(props: LegacyComboboxProps): React.ReactElement
|
||||
@@ -69,6 +70,7 @@ function Combobox(
|
||||
emptyText={props.emptyText}
|
||||
className={props.className}
|
||||
allowCustomValue={props.allowCustomValue}
|
||||
openOnFocus={props.openOnFocus}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user