mirror of
				https://github.com/langgenius/dify.git
				synced 2025-11-03 20:33:00 +00:00 
			
		
		
		
	feat: Update selection handling to support multiple choice in OnlineDocuments and PageSelector components
This commit is contained in:
		
							parent
							
								
									55d7d7ef76
								
							
						
					
					
						commit
						2d0d448667
					
				@ -265,7 +265,6 @@ const CreateFormPipeline = () => {
 | 
			
		||||
                      nodeData={datasource!.nodeData}
 | 
			
		||||
                      pageIdList={onlineDocuments.map(doc => doc.page_id)}
 | 
			
		||||
                      onSelect={updateOnlineDocuments}
 | 
			
		||||
                      canPreview
 | 
			
		||||
                      onPreview={updateCurrentPage}
 | 
			
		||||
                    />
 | 
			
		||||
                  )}
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,6 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-so
 | 
			
		||||
type OnlineDocumentsProps = {
 | 
			
		||||
  pageIdList?: string[]
 | 
			
		||||
  onSelect: (selectedPages: NotionPage[]) => void
 | 
			
		||||
  canPreview?: boolean
 | 
			
		||||
  previewPageId?: string
 | 
			
		||||
  onPreview?: (selectedPage: NotionPage) => void
 | 
			
		||||
  isInPipeline?: boolean
 | 
			
		||||
@ -25,7 +24,6 @@ type OnlineDocumentsProps = {
 | 
			
		||||
const OnlineDocuments = ({
 | 
			
		||||
  pageIdList,
 | 
			
		||||
  onSelect,
 | 
			
		||||
  canPreview,
 | 
			
		||||
  previewPageId,
 | 
			
		||||
  onPreview,
 | 
			
		||||
  isInPipeline = false,
 | 
			
		||||
@ -97,19 +95,22 @@ const OnlineDocuments = ({
 | 
			
		||||
  const handleSearchValueChange = useCallback((value: string) => {
 | 
			
		||||
    setSearchValue(value)
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  const handleSelectWorkspace = useCallback((workspaceId: string) => {
 | 
			
		||||
    setCurrentWorkspaceId(workspaceId)
 | 
			
		||||
  }, [])
 | 
			
		||||
  const handleSelectPages = (newSelectedPagesId: Set<string>) => {
 | 
			
		||||
 | 
			
		||||
  const handleSelectPages = useCallback((newSelectedPagesId: Set<string>) => {
 | 
			
		||||
    const selectedPages = Array.from(newSelectedPagesId).map(pageId => PagesMapAndSelectedPagesId[0][pageId])
 | 
			
		||||
 | 
			
		||||
    setSelectedPagesId(new Set(Array.from(newSelectedPagesId)))
 | 
			
		||||
    onSelect(selectedPages)
 | 
			
		||||
  }
 | 
			
		||||
  const handlePreviewPage = (previewPageId: string) => {
 | 
			
		||||
  }, [onSelect, PagesMapAndSelectedPagesId])
 | 
			
		||||
 | 
			
		||||
  const handlePreviewPage = useCallback((previewPageId: string) => {
 | 
			
		||||
    if (onPreview)
 | 
			
		||||
      onPreview(PagesMapAndSelectedPagesId[0][previewPageId])
 | 
			
		||||
  }
 | 
			
		||||
  }, [PagesMapAndSelectedPagesId, onPreview])
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setCurrentWorkspaceId(firstWorkspaceId)
 | 
			
		||||
@ -154,9 +155,10 @@ const OnlineDocuments = ({
 | 
			
		||||
            list={currentWorkspace?.pages || []}
 | 
			
		||||
            pagesMap={PagesMapAndSelectedPagesId[0]}
 | 
			
		||||
            onSelect={handleSelectPages}
 | 
			
		||||
            canPreview={canPreview}
 | 
			
		||||
            canPreview={!isInPipeline}
 | 
			
		||||
            previewPageId={previewPageId}
 | 
			
		||||
            onPreview={handlePreviewPage}
 | 
			
		||||
            isMultipleChoice={!isInPipeline}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { useMemo, useState } from 'react'
 | 
			
		||||
import { useCallback, useEffect, useMemo, useState } from 'react'
 | 
			
		||||
import { useTranslation } from 'react-i18next'
 | 
			
		||||
import { FixedSizeList as List } from 'react-window'
 | 
			
		||||
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
 | 
			
		||||
@ -15,6 +15,7 @@ type PageSelectorProps = {
 | 
			
		||||
  canPreview?: boolean
 | 
			
		||||
  previewPageId?: string
 | 
			
		||||
  onPreview?: (selectedPageId: string) => void
 | 
			
		||||
  isMultipleChoice?: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type NotionPageTreeItem = {
 | 
			
		||||
@ -41,21 +42,26 @@ const PageSelector = ({
 | 
			
		||||
  canPreview = true,
 | 
			
		||||
  previewPageId,
 | 
			
		||||
  onPreview,
 | 
			
		||||
  isMultipleChoice = true,
 | 
			
		||||
}: PageSelectorProps) => {
 | 
			
		||||
  const { t } = useTranslation()
 | 
			
		||||
  const [prevDataList, setPrevDataList] = useState(list)
 | 
			
		||||
  const [dataList, setDataList] = useState<NotionPageItem[]>([])
 | 
			
		||||
  const [localPreviewPageId, setLocalPreviewPageId] = useState('')
 | 
			
		||||
  if (prevDataList !== list) {
 | 
			
		||||
    setPrevDataList(list)
 | 
			
		||||
    setDataList(list.filter(item => item.parent_id === 'root' || !pagesMap[item.parent_id]).map((item) => {
 | 
			
		||||
      return {
 | 
			
		||||
        ...item,
 | 
			
		||||
        expand: false,
 | 
			
		||||
        depth: 0,
 | 
			
		||||
      }
 | 
			
		||||
    }))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (prevDataList !== list) {
 | 
			
		||||
      setPrevDataList(list)
 | 
			
		||||
      setDataList(list.filter(item => item.parent_id === 'root' || !pagesMap[item.parent_id]).map((item) => {
 | 
			
		||||
        return {
 | 
			
		||||
          ...item,
 | 
			
		||||
          expand: false,
 | 
			
		||||
          depth: 0,
 | 
			
		||||
        }
 | 
			
		||||
      }))
 | 
			
		||||
    }
 | 
			
		||||
  }, [prevDataList, list, pagesMap])
 | 
			
		||||
 | 
			
		||||
  const searchDataList = list.filter((item) => {
 | 
			
		||||
    return item.page_name.includes(searchValue)
 | 
			
		||||
  }).map((item) => {
 | 
			
		||||
@ -79,7 +85,7 @@ const PageSelector = ({
 | 
			
		||||
    }, {})
 | 
			
		||||
  }, [list, pagesMap])
 | 
			
		||||
 | 
			
		||||
  const handleToggle = (index: number) => {
 | 
			
		||||
  const handleToggle = useCallback((index: number) => {
 | 
			
		||||
    const current = dataList[index]
 | 
			
		||||
    const pageId = current.page_id
 | 
			
		||||
    const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId]
 | 
			
		||||
@ -105,16 +111,16 @@ const PageSelector = ({
 | 
			
		||||
        ...dataList.slice(index + 1)]
 | 
			
		||||
    }
 | 
			
		||||
    setDataList(newDataList)
 | 
			
		||||
  }
 | 
			
		||||
  }, [dataList, listMapWithChildrenAndDescendants, pagesMap])
 | 
			
		||||
 | 
			
		||||
  const copyValue = new Set([...value])
 | 
			
		||||
  const handleCheck = (index: number) => {
 | 
			
		||||
  const handleCheck = useCallback((index: number) => {
 | 
			
		||||
    const copyValue = new Set([...value])
 | 
			
		||||
    const current = currentDataList[index]
 | 
			
		||||
    const pageId = current.page_id
 | 
			
		||||
    const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId]
 | 
			
		||||
 | 
			
		||||
    if (copyValue.has(pageId)) {
 | 
			
		||||
      if (!searchValue) {
 | 
			
		||||
      if (!searchValue && isMultipleChoice) {
 | 
			
		||||
        for (const item of currentWithChildrenAndDescendants.descendants)
 | 
			
		||||
          copyValue.delete(item)
 | 
			
		||||
      }
 | 
			
		||||
@ -122,18 +128,24 @@ const PageSelector = ({
 | 
			
		||||
      copyValue.delete(pageId)
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      if (!searchValue) {
 | 
			
		||||
      if (!searchValue && isMultipleChoice) {
 | 
			
		||||
        for (const item of currentWithChildrenAndDescendants.descendants)
 | 
			
		||||
          copyValue.add(item)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      copyValue.add(pageId)
 | 
			
		||||
      // Single choice mode, clear previous selection
 | 
			
		||||
      if (!isMultipleChoice && copyValue.size > 0) {
 | 
			
		||||
        copyValue.clear()
 | 
			
		||||
        copyValue.add(pageId)
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        copyValue.add(pageId)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onSelect(new Set([...copyValue]))
 | 
			
		||||
  }
 | 
			
		||||
  }, [currentDataList, isMultipleChoice, listMapWithChildrenAndDescendants, onSelect, searchValue, value])
 | 
			
		||||
 | 
			
		||||
  const handlePreview = (index: number) => {
 | 
			
		||||
  const handlePreview = useCallback((index: number) => {
 | 
			
		||||
    const current = currentDataList[index]
 | 
			
		||||
    const pageId = current.page_id
 | 
			
		||||
 | 
			
		||||
@ -141,7 +153,7 @@ const PageSelector = ({
 | 
			
		||||
 | 
			
		||||
    if (onPreview)
 | 
			
		||||
      onPreview(pageId)
 | 
			
		||||
  }
 | 
			
		||||
  }, [currentDataList, onPreview])
 | 
			
		||||
 | 
			
		||||
  if (!currentDataList.length) {
 | 
			
		||||
    return (
 | 
			
		||||
@ -171,6 +183,7 @@ const PageSelector = ({
 | 
			
		||||
        searchValue,
 | 
			
		||||
        previewPageId: currentPreviewPageId,
 | 
			
		||||
        pagesMap,
 | 
			
		||||
        isMultipleChoice,
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      {Item}
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ import Checkbox from '@/app/components/base/checkbox'
 | 
			
		||||
import NotionIcon from '@/app/components/base/notion-icon'
 | 
			
		||||
import cn from '@/utils/classnames'
 | 
			
		||||
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
 | 
			
		||||
import Radio from '@/app/components/base/radio/ui'
 | 
			
		||||
 | 
			
		||||
type NotionPageTreeItem = {
 | 
			
		||||
  children: Set<string>
 | 
			
		||||
@ -34,6 +35,7 @@ const Item = ({ index, style, data }: ListChildComponentProps<{
 | 
			
		||||
  searchValue: string
 | 
			
		||||
  previewPageId: string
 | 
			
		||||
  pagesMap: DataSourceNotionPageMap
 | 
			
		||||
  isMultipleChoice?: boolean
 | 
			
		||||
}>) => {
 | 
			
		||||
  const { t } = useTranslation()
 | 
			
		||||
  const {
 | 
			
		||||
@ -48,6 +50,7 @@ const Item = ({ index, style, data }: ListChildComponentProps<{
 | 
			
		||||
    searchValue,
 | 
			
		||||
    previewPageId,
 | 
			
		||||
    pagesMap,
 | 
			
		||||
    isMultipleChoice,
 | 
			
		||||
  } = data
 | 
			
		||||
  const current = dataList[index]
 | 
			
		||||
  const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[current.page_id]
 | 
			
		||||
@ -88,16 +91,24 @@ const Item = ({ index, style, data }: ListChildComponentProps<{
 | 
			
		||||
        previewPageId === current.page_id && 'bg-state-base-hover')}
 | 
			
		||||
      style={{ ...style, top: style.top as number + 8, left: 8, right: 8, width: 'calc(100% - 16px)' }}
 | 
			
		||||
    >
 | 
			
		||||
      <Checkbox
 | 
			
		||||
        className='mr-2 shrink-0'
 | 
			
		||||
        checked={checkedIds.has(current.page_id)}
 | 
			
		||||
        disabled={disabled}
 | 
			
		||||
        onCheck={() => {
 | 
			
		||||
          if (disabled)
 | 
			
		||||
            return
 | 
			
		||||
          handleCheck(index)
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
      {isMultipleChoice ? (
 | 
			
		||||
        <Checkbox
 | 
			
		||||
          className='mr-2 shrink-0'
 | 
			
		||||
          checked={checkedIds.has(current.page_id)}
 | 
			
		||||
          disabled={disabled}
 | 
			
		||||
          onCheck={() => {
 | 
			
		||||
            handleCheck(index)
 | 
			
		||||
          }}
 | 
			
		||||
        />) : (
 | 
			
		||||
        <Radio
 | 
			
		||||
          className='mr-2 shrink-0'
 | 
			
		||||
          isChecked={checkedIds.has(current.page_id)}
 | 
			
		||||
          disabled={disabled}
 | 
			
		||||
          onCheck={() => {
 | 
			
		||||
            handleCheck(index)
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
      {!searchValue && renderArrow()}
 | 
			
		||||
      <NotionIcon
 | 
			
		||||
        className='mr-1 shrink-0'
 | 
			
		||||
 | 
			
		||||
@ -169,7 +169,7 @@ const WebsiteCrawl = ({
 | 
			
		||||
              usedTime={Number.parseFloat(crawlResult?.time_consuming as string) || 0}
 | 
			
		||||
              previewIndex={previewIndex}
 | 
			
		||||
              onPreview={onPreview}
 | 
			
		||||
              isMultipleChoice={isInPipeline} // only support single choice in test run
 | 
			
		||||
              isMultipleChoice={!isInPipeline} // only support single choice in test run
 | 
			
		||||
            />
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user