Files
coze-studio/frontend/packages/common/editor-plugins/src/input-slot/input-slot-widget/config-mode-popover.tsx
2025-07-31 23:15:48 +08:00

107 lines
3.4 KiB
TypeScript

/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useEffect, useState } from 'react';
import { useEditor } from '@coze-editor/editor/react';
import { type EditorAPI } from '@coze-editor/editor/preset-prompt';
import { type ViewUpdate } from '@codemirror/view';
import { InputConfigPopover } from '../input-config-popover';
import { type TemplateParser } from '../../shared/utils/template-parser';
export const ConfigModeWidgetPopover = (props: {
direction: 'bottomLeft' | 'topLeft' | 'bottomRight' | 'topRight';
templateParser: TemplateParser;
}) => {
const { direction, templateParser } = props;
const editor = useEditor<EditorAPI>();
const [placeholder, setPlaceholder] = useState('');
const [value, setValue] = useState('');
const [configPopoverVisible, setConfigPopoverVisible] = useState(false);
const [popoverPosition, setPopoverPosition] = useState(-1);
useEffect(() => {
if (!editor) {
return;
}
const handleViewUpdate = (e: ViewUpdate) => {
if (e.docChanged) {
// Determine whether the current cursor is in the slot node
const { state } = e;
const range = templateParser.getCursorInMarkNodeRange(state);
if (!range) {
return;
}
const content = state.sliceDoc(range.open.to, range.close.from);
if (content === value) {
return;
}
setValue(content);
}
if (e.selectionSet) {
const { state } = e;
const range = templateParser.getCursorInMarkNodeRange(state);
if (!range) {
setPopoverPosition(-1);
setConfigPopoverVisible(false);
return;
}
const content = templateParser.getCursorTemplateContent(editor);
const { placeholder: configPlaceholder } =
templateParser.getCursorTemplateData(state) ?? {};
setPlaceholder(configPlaceholder);
setValue(content ?? '');
setPopoverPosition(range.open.from);
setConfigPopoverVisible(true);
}
};
editor.$on('viewUpdate', handleViewUpdate);
return () => {
editor.$off('viewUpdate', handleViewUpdate);
};
}, [editor, value]);
const handlePlaceholderChange = (configPlaceholder: string) => {
if (!editor || !configPopoverVisible) {
return;
}
setPlaceholder(configPlaceholder);
templateParser.updateCursorTemplateData(editor, {
placeholder: configPlaceholder,
});
};
const handleValueChange = (configValue: string) => {
if (!editor || !configPopoverVisible) {
return;
}
setValue(configValue);
templateParser.updateCursorTemplateContent(editor, configValue);
};
return (
<InputConfigPopover
visible={configPopoverVisible}
positon={popoverPosition}
direction={direction}
placeholder={placeholder}
value={value}
onPlaceholderChange={handlePlaceholderChange}
onValueChange={handleValueChange}
/>
);
};