mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 16:36:28 +08:00
chore: portoal to follow to popover
This commit is contained in:
@@ -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<SetStateAction<HTMLElement | null>> | ((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(
|
||||
<PortalToFollowElem
|
||||
open
|
||||
placement="bottom-start"
|
||||
offset={4}
|
||||
onOpenChange={(open) => {
|
||||
if (!open)
|
||||
closeMenu()
|
||||
}}
|
||||
>
|
||||
<PortalToFollowElemTrigger asChild>
|
||||
<span className="inline-block h-0 w-0" />
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className="z-[1000]">
|
||||
<>
|
||||
<ReferenceSync anchor={anchorElementRef.current} setReference={refs.setReference} />
|
||||
<div
|
||||
ref={refs.setFloating}
|
||||
style={{
|
||||
...floatingStyles,
|
||||
visibility: isPositioned ? 'visible' : 'hidden',
|
||||
}}
|
||||
className="z-[1002] outline-none"
|
||||
>
|
||||
<FilePickerPanel
|
||||
onSelectNode={(node) => {
|
||||
insertFileReference(node.id)
|
||||
closeMenu()
|
||||
}}
|
||||
/>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>,
|
||||
</div>
|
||||
</>,
|
||||
anchorElementRef.current,
|
||||
)
|
||||
}, [insertFileReference, options])
|
||||
}, [floatingStyles, insertFileReference, isPositioned, options, refs])
|
||||
|
||||
return (
|
||||
<LexicalTypeaheadMenuPlugin
|
||||
|
||||
@@ -9,12 +9,11 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import FileTypeIcon from '@/app/components/base/file-uploader/file-type-icon'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { useSelectOrDelete } from '@/app/components/base/prompt-editor/hooks'
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
} from '@/app/components/base/ui/popover'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
|
||||
import { START_TAB_ID } from '@/app/components/workflow/skill/constants'
|
||||
import { useSkillAssetNodeMap } from '@/app/components/workflow/skill/hooks/file-tree/data/use-skill-asset-tree'
|
||||
@@ -162,13 +161,11 @@ const FileReferenceBlock = ({ nodeKey, resourceId }: FileReferenceBlockProps) =>
|
||||
}, [previewOpen, ref, shouldPreview, updatePreviewPosition])
|
||||
|
||||
const fileBlock = (
|
||||
<PortalToFollowElem
|
||||
<Popover
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement="bottom-start"
|
||||
offset={4}
|
||||
>
|
||||
<PortalToFollowElemTrigger ref={ref} className="inline-flex">
|
||||
<div ref={ref} className="inline-flex">
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
disabled={!tooltipContent}
|
||||
@@ -183,6 +180,7 @@ const FileReferenceBlock = ({ nodeKey, resourceId }: FileReferenceBlockProps) =>
|
||||
onMouseDown={() => {
|
||||
if (!isInteractive)
|
||||
return
|
||||
|
||||
setOpen(prev => !prev)
|
||||
}}
|
||||
>
|
||||
@@ -212,14 +210,19 @@ const FileReferenceBlock = ({ nodeKey, resourceId }: FileReferenceBlockProps) =>
|
||||
<TooltipContent>{tooltipContent}</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className="z-[1000]">
|
||||
</div>
|
||||
<PopoverContent
|
||||
placement="bottom-start"
|
||||
sideOffset={4}
|
||||
popupClassName="rounded-none border-none bg-transparent shadow-none"
|
||||
positionerProps={{ anchor: ref }}
|
||||
>
|
||||
<FilePickerPanel
|
||||
onSelectNode={handleSelect}
|
||||
focusNodeId={resourceId}
|
||||
/>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
|
||||
if (!shouldPreview)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user