From 43b10422850c19893b30617394ecb07be8ee0304 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 27 Mar 2026 14:58:12 +0800 Subject: [PATCH] chore: portoal to follow to popover --- .../plugins/file-picker-block.tsx | 64 ++++++++++------ .../file-reference-block/component.tsx | 29 ++++---- web/eslint-suppressions.json | 74 ++++++++----------- 3 files changed, 90 insertions(+), 77 deletions(-) diff --git a/web/app/components/workflow/skill/editor/skill-editor/plugins/file-picker-block.tsx b/web/app/components/workflow/skill/editor/skill-editor/plugins/file-picker-block.tsx index c8216141f16..354fcba01ec 100644 --- a/web/app/components/workflow/skill/editor/skill-editor/plugins/file-picker-block.tsx +++ b/web/app/components/workflow/skill/editor/skill-editor/plugins/file-picker-block.tsx @@ -1,17 +1,19 @@ import type { LexicalNode } from 'lexical' +import type { Dispatch, SetStateAction } from 'react' +import { + flip, + offset, + shift, + useFloating, +} from '@floating-ui/react' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' import { LexicalTypeaheadMenuPlugin, MenuOption } from '@lexical/react/LexicalTypeaheadMenuPlugin' import { $insertNodes, } from 'lexical' import * as React from 'react' -import { useCallback, useMemo } from 'react' +import { useCallback, useLayoutEffect, useMemo } from 'react' import ReactDOM from 'react-dom' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' import { useBasicTypeaheadTriggerMatch } from '@/app/components/base/prompt-editor/hooks' import { $splitNodeContainingQuery } from '@/app/components/base/prompt-editor/utils' import { FilePickerPanel } from './file-picker-panel' @@ -23,8 +25,29 @@ class FilePickerMenuOption extends MenuOption { } } +type ReferenceSyncProps = { + anchor: HTMLElement | null + setReference: Dispatch> | ((node: HTMLElement | null) => void) +} + +const ReferenceSync = ({ anchor, setReference }: ReferenceSyncProps) => { + useLayoutEffect(() => { + setReference(anchor) + }, [anchor, setReference]) + + return null +} + const FilePickerBlock = () => { const [editor] = useLexicalComposerContext() + const { refs, floatingStyles, isPositioned } = useFloating({ + placement: 'bottom-start', + middleware: [ + offset(0), + shift({ padding: 8 }), + flip(), + ], + }) const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', { minLength: 0, maxLength: 0, @@ -54,30 +77,27 @@ const FilePickerBlock = () => { const closeMenu = () => selectOptionAndCleanUp(options[0]) return ReactDOM.createPortal( - { - if (!open) - closeMenu() - }} - > - - - - + <> + +
{ insertFileReference(node.id) closeMenu() }} /> - - , +
+ , anchorElementRef.current, ) - }, [insertFileReference, options]) + }, [floatingStyles, insertFileReference, isPositioned, options, refs]) return ( }, [previewOpen, ref, shouldPreview, updatePreviewPosition]) const fileBlock = ( - - +
onMouseDown={() => { if (!isInteractive) return + setOpen(prev => !prev) }} > @@ -212,14 +210,19 @@ const FileReferenceBlock = ({ nodeKey, resourceId }: FileReferenceBlockProps) => {tooltipContent} )} - - +
+ -
-
+ + ) if (!shouldPreview) diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index 58aa711c228..c2b8068cdcf 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -5971,21 +5971,11 @@ "count": 2 } }, - "app/components/workflow/skill/editor/skill-editor/plugins/file-picker-block.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "app/components/workflow/skill/editor/skill-editor/plugins/file-picker-upload-modal.tsx": { "no-restricted-imports": { "count": 1 } }, - "app/components/workflow/skill/editor/skill-editor/plugins/file-reference-block/component.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "app/components/workflow/skill/viewer/sqlite-file-preview/table-selector.tsx": { "no-restricted-imports": { "count": 1 @@ -6568,14 +6558,42 @@ "count": 1 } }, - "utils/clipboard.ts": { + "utils/__tests__/completion-params.spec.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "utils/__tests__/get-icon.spec.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "utils/__tests__/index.spec.ts": { + "test/no-identical-title": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 8 + } + }, + "utils/__tests__/model-config.spec.ts": { + "ts/no-explicit-any": { + "count": 13 + } + }, + "utils/__tests__/navigation.spec.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "utils/__tests__/tool-call.spec.ts": { "ts/no-explicit-any": { "count": 1 } }, - "utils/__tests__/completion-params.spec.ts": { + "utils/clipboard.ts": { "ts/no-explicit-any": { - "count": 3 + "count": 1 } }, "utils/completion-params.ts": { @@ -6596,24 +6614,11 @@ "count": 1 } }, - "utils/__tests__/get-icon.spec.ts": { - "ts/no-explicit-any": { - "count": 2 - } - }, "utils/gtag.ts": { "ts/no-explicit-any": { "count": 2 } }, - "utils/__tests__/index.spec.ts": { - "test/no-identical-title": { - "count": 2 - }, - "ts/no-explicit-any": { - "count": 8 - } - }, "utils/index.ts": { "ts/no-explicit-any": { "count": 3 @@ -6624,29 +6629,14 @@ "count": 1 } }, - "utils/__tests__/model-config.spec.ts": { - "ts/no-explicit-any": { - "count": 13 - } - }, "utils/model-config.ts": { "ts/no-explicit-any": { "count": 6 } }, - "utils/__tests__/navigation.spec.ts": { - "ts/no-explicit-any": { - "count": 4 - } - }, - "utils/__tests__/tool-call.spec.ts": { - "ts/no-explicit-any": { - "count": 1 - } - }, "utils/validators.ts": { "ts/no-explicit-any": { "count": 2 } } -} +} \ No newline at end of file