feat(dataflow-result): scroll to and highlight newly created chunk (#16937)

This commit is contained in:
chanx
2026-07-15 14:33:36 +08:00
committed by GitHub
parent 14d361aa51
commit 30cc00bed2
4 changed files with 50 additions and 2 deletions

View File

@@ -13,6 +13,7 @@ const FormatPreserveEditor = ({
textMode,
clickChunk,
isReadonly,
newChunkIndex,
}: FormatPreserveEditorProps) => {
console.log('initialValue', initialValue);
@@ -41,6 +42,7 @@ const FormatPreserveEditor = ({
textMode={textMode}
isChunck={isChunck}
clickChunk={clickChunk}
newChunkIndex={newChunkIndex}
/>
)}

View File

@@ -20,6 +20,7 @@ export interface FormatPreserveEditorProps {
textMode?: ChunkTextMode;
clickChunk: (chunk: IChunk) => void;
isReadonly: boolean;
newChunkIndex?: number;
}
export type IJsonContainerProps = {
@@ -47,6 +48,7 @@ export type IJsonContainerProps = {
textMode?: ChunkTextMode;
clickChunk: (chunk: IChunk) => void;
isReadonly: boolean;
newChunkIndex?: number;
};
export type IObjContainerProps = {

View File

@@ -1,7 +1,7 @@
import { Checkbox } from '@/components/ui/checkbox';
import { cn } from '@/lib/utils';
import { isArray } from 'lodash';
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { ChunkTextMode } from '../../constant';
import styles from '../../index.module.less';
import { IChunk } from '../../interface';
@@ -23,11 +23,14 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
textMode,
clickChunk,
isReadonly,
newChunkIndex,
} = props;
const { content, activeEditIndex, setActiveEditIndex, editDivRef } =
useParserInit({ initialValue });
const sectionRefs = useRef<(HTMLElement | null)[]>([]);
const parserKey = useMemo(() => {
const key =
content.key === 'chunks' && content.params.field_name
@@ -76,6 +79,32 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
}
}, [editDivRef, activeEditIndex, content, parserKey]);
useEffect(() => {
if (newChunkIndex === undefined) return;
const target = sectionRefs.current[newChunkIndex];
if (!target) return;
// Scroll only the nearest scrollable ancestor so the whole page doesn't shift
let scrollContainer: HTMLElement | null = target.parentElement;
while (scrollContainer) {
const { overflowY } = window.getComputedStyle(scrollContainer);
if (overflowY === 'auto' || overflowY === 'scroll') break;
scrollContainer = scrollContainer.parentElement;
}
if (scrollContainer) {
const containerRect = scrollContainer.getBoundingClientRect();
const targetRect = target.getBoundingClientRect();
const offsetTop =
targetRect.top - containerRect.top + scrollContainer.scrollTop;
const targetCenter = offsetTop + target.clientHeight / 2;
scrollContainer.scrollTo({
top: Math.max(0, targetCenter - scrollContainer.clientHeight / 2),
behavior: 'smooth',
});
}
}, [newChunkIndex, content]);
return (
<>
{isArray(content.value) &&
@@ -90,11 +119,17 @@ export const ArrayContainer = (props: IJsonContainerProps) => {
return (
<section
key={index}
ref={(el) => {
sectionRefs.current[index] = el;
}}
className={cn(
isChunck
? 'bg-bg-card my-2 p-2 rounded-lg flex gap-1 items-start'
? 'bg-bg-card my-2 p-2 rounded-lg flex gap-1 items-start transition-shadow duration-300'
: '',
activeEditIndex === index && isChunck ? 'bg-bg-title' : '',
newChunkIndex === index && isChunck
? 'ring-2 ring-accent-primary'
: '',
)}
>
{isChunck && !isReadonly && (

View File

@@ -36,6 +36,7 @@ const ParserContainer = (props: IProps) => {
} = props;
const { t } = useTranslation();
const [selectedChunkIds, setSelectedChunkIds] = useState<string[]>([]);
const [newChunkIndex, setNewChunkIndex] = useState<number | undefined>();
const { changeChunkTextMode, textMode } = useChangeChunkTextMode();
const initialValue = useMemo(() => {
const outputs = data?.value?.obj?.params?.outputs;
@@ -136,6 +137,7 @@ const ParserContainer = (props: IProps) => {
const handleCreateChunk = useCallback(
(text: string) => {
const newText = [...initialText.value, { text: text || ' ' }];
setNewChunkIndex(newText.length - 1);
setInitialText({
...initialText,
value: newText as any,
@@ -144,6 +146,12 @@ const ParserContainer = (props: IProps) => {
[initialText],
);
useEffect(() => {
if (newChunkIndex === undefined) return;
const timer = setTimeout(() => setNewChunkIndex(undefined), 3000);
return () => clearTimeout(timer);
}, [newChunkIndex]);
return (
<>
{isChange && !isReadonly && (
@@ -220,6 +228,7 @@ const ParserContainer = (props: IProps) => {
clickChunk={clickChunk}
handleCheckboxClick={handleCheckboxClick}
selectedChunkIds={selectedChunkIds}
newChunkIndex={newChunkIndex}
/>
)}
<Spotlight opcity={0.6} coverage={60} />