mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 12:20:34 +00:00
perf: variable label picker scroll & focus disappear (#2135)
* fix: variable label picker scroll & focus disappear * fix * fix filter * revert
This commit is contained in:
@@ -240,7 +240,7 @@ export function replaceVariableLabel({
|
||||
variables: Record<string, string | number>;
|
||||
runningNode: RuntimeNodeItemType;
|
||||
}) {
|
||||
if (!(typeof text === 'string')) return text;
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
const globalVariables = Object.keys(variables).map((key) => {
|
||||
return {
|
||||
|
@@ -292,14 +292,14 @@ async function fetchData({
|
||||
function replaceVariable(text: string, obj: Record<string, any>) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === undefined) {
|
||||
text = text.replace(new RegExp(`{{${key}}}`, 'g'), UNDEFINED_SIGN);
|
||||
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), UNDEFINED_SIGN);
|
||||
} else {
|
||||
const replacement = JSON.stringify(value);
|
||||
const unquotedReplacement =
|
||||
replacement.startsWith('"') && replacement.endsWith('"')
|
||||
? replacement.slice(1, -1)
|
||||
: replacement;
|
||||
text = text.replace(new RegExp(`{{${key}}}`, 'g'), unquotedReplacement);
|
||||
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), unquotedReplacement);
|
||||
}
|
||||
}
|
||||
return text || '';
|
||||
|
@@ -129,7 +129,7 @@ export default function Editor({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<VariableLabelPickerPlugin variables={variables} />
|
||||
<VariableLabelPickerPlugin variables={variables} isFocus={focus} />
|
||||
<VariablePlugin variables={variables} />
|
||||
<VariableLabelPlugin variables={variables} />
|
||||
<OnBlurPlugin onBlur={onBlur} />
|
||||
|
@@ -27,13 +27,18 @@ interface TransformedParent {
|
||||
}
|
||||
|
||||
export default function VariableLabelPickerPlugin({
|
||||
variables
|
||||
variables,
|
||||
isFocus
|
||||
}: {
|
||||
variables: EditorVariablePickerType[];
|
||||
isFocus: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [editor] = useLexicalComposerContext();
|
||||
const [queryString, setQueryString] = useState<string | null>(null);
|
||||
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
||||
const [highlightIndex, setHighlightIndex] = useState<number | null>(null);
|
||||
const highlightedItemRef = React.useRef<any>(null);
|
||||
|
||||
const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', {
|
||||
minLength: 0
|
||||
@@ -58,6 +63,15 @@ export default function VariableLabelPickerPlugin({
|
||||
[editor]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (highlightedItemRef.current) {
|
||||
highlightedItemRef.current.scrollIntoView({
|
||||
behavior: 'auto',
|
||||
block: 'end'
|
||||
});
|
||||
}
|
||||
}, [currentIndex]);
|
||||
|
||||
return (
|
||||
<LexicalTypeaheadMenuPlugin
|
||||
onQueryChange={setQueryString}
|
||||
@@ -71,7 +85,11 @@ export default function VariableLabelPickerPlugin({
|
||||
if (anchorElementRef.current == null) {
|
||||
return null;
|
||||
}
|
||||
return anchorElementRef.current && variables.length
|
||||
if (currentIndex !== selectedIndex) {
|
||||
setCurrentIndex(selectedIndex || 0);
|
||||
setHighlightIndex(selectedIndex || 0);
|
||||
}
|
||||
return anchorElementRef.current && variables.length && isFocus
|
||||
? ReactDOM.createPortal(
|
||||
<Box
|
||||
bg={'white'}
|
||||
@@ -123,7 +141,7 @@ export default function VariableLabelPickerPlugin({
|
||||
{item.label}
|
||||
</Box>
|
||||
</Flex>
|
||||
{item.children?.map((child, index) => (
|
||||
{item.children?.map((child) => (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
as={'li'}
|
||||
@@ -136,7 +154,8 @@ export default function VariableLabelPickerPlugin({
|
||||
_notLast={{
|
||||
mb: 1
|
||||
}}
|
||||
{...(selectedIndex === child.index
|
||||
ref={selectedIndex === child.index ? highlightedItemRef : null}
|
||||
{...(highlightIndex === child.index
|
||||
? {
|
||||
bg: '#1118240D',
|
||||
color: 'primary.700'
|
||||
@@ -145,12 +164,11 @@ export default function VariableLabelPickerPlugin({
|
||||
bg: 'white',
|
||||
color: 'myGray.600'
|
||||
})}
|
||||
onClick={() => {
|
||||
setHighlightedIndex(child.index);
|
||||
onMouseDown={() => {
|
||||
selectOptionAndCleanUp({ ...child, parent: item });
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setHighlightedIndex(child.index);
|
||||
setHighlightIndex(child.index);
|
||||
}}
|
||||
>
|
||||
<Box ml={2} fontSize={'sm'} whiteSpace={'nowrap'}>
|
||||
|
@@ -221,7 +221,7 @@ export function getHashtagRegexString(): string {
|
||||
const hashtag =
|
||||
`(${hashLeftCharList})` +
|
||||
`(${hashLeftCharList})` +
|
||||
`(${hashMiddleCharList})([a-zA-Z0-9_\\.]{0,29})(${hashMiddleCharList})` +
|
||||
`(${hashMiddleCharList})([a-zA-Z0-9_\\.]{0,100})(${hashMiddleCharList})` +
|
||||
`(${hashRightCharList})(${hashRightCharList})`;
|
||||
|
||||
return hashtag;
|
||||
|
@@ -35,6 +35,7 @@ type useChatStoreType = OutLinkChatAuthProps &
|
||||
ChatProviderProps & {
|
||||
welcomeText: string;
|
||||
variableList: VariableItemType[];
|
||||
allVariableList: VariableItemType[];
|
||||
questionGuide: boolean;
|
||||
ttsConfig: AppTTSConfigType;
|
||||
whisperConfig: AppWhisperConfigType;
|
||||
@@ -189,6 +190,7 @@ const Provider = ({
|
||||
teamToken,
|
||||
welcomeText,
|
||||
variableList: variables.filter((item) => item.type !== VariableInputEnum.custom),
|
||||
allVariableList: variables,
|
||||
questionGuide,
|
||||
ttsConfig,
|
||||
whisperConfig,
|
||||
|
@@ -21,7 +21,7 @@ const VariableInput = ({
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { appAvatar, variableList, variablesForm } = useContextSelector(ChatBoxContext, (v) => v);
|
||||
const { register, getValues, setValue, handleSubmit: handleSubmitChat, control } = variablesForm;
|
||||
const { register, setValue, handleSubmit: handleSubmitChat, control } = variablesForm;
|
||||
|
||||
return (
|
||||
<Box py={3}>
|
||||
|
@@ -140,6 +140,7 @@ const ChatBox = (
|
||||
const {
|
||||
welcomeText,
|
||||
variableList,
|
||||
allVariableList,
|
||||
questionGuide,
|
||||
startSegmentedAudio,
|
||||
finishSegmentedAudio,
|
||||
@@ -390,7 +391,7 @@ const ChatBox = (
|
||||
|
||||
// delete invalid variables, 只保留在 variableList 中的变量
|
||||
const requestVariables: Record<string, any> = {};
|
||||
variableList?.forEach((item) => {
|
||||
allVariableList?.forEach((item) => {
|
||||
requestVariables[item.key] = variables[item.key] || '';
|
||||
});
|
||||
|
||||
|
@@ -10,7 +10,6 @@ import {
|
||||
storeNodes2RuntimeNodes
|
||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from './context';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
@@ -21,6 +20,7 @@ import dynamic from 'next/dynamic';
|
||||
import { useChat } from '@/components/core/chat/ChatContainer/useChat';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
|
||||
|
||||
|
@@ -132,8 +132,9 @@ const MyApps = () => {
|
||||
</Box>
|
||||
)}
|
||||
<Flex gap={5} flex={'1 0 0'} h={0}>
|
||||
<Box
|
||||
<Flex
|
||||
flex={'1 0 0'}
|
||||
flexDirection={'column'}
|
||||
h={'100%'}
|
||||
pr={folderDetail ? [4, 2] : [4, 10]}
|
||||
pl={3}
|
||||
@@ -237,7 +238,7 @@ const MyApps = () => {
|
||||
<MyBox flex={'1 0 0'} isLoading={myApps.length === 0 && isFetchingApps}>
|
||||
<List />
|
||||
</MyBox>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
{/* Folder slider */}
|
||||
{!!folderDetail && isPc && (
|
||||
|
Reference in New Issue
Block a user