feat: Update selection handling to support multiple choice in OnlineDocuments and PageSelector components

This commit is contained in:
twwu 2025-07-01 14:14:28 +08:00
parent 55d7d7ef76
commit 2d0d448667
5 changed files with 66 additions and 41 deletions

View File

@ -265,7 +265,6 @@ const CreateFormPipeline = () => {
nodeData={datasource!.nodeData} nodeData={datasource!.nodeData}
pageIdList={onlineDocuments.map(doc => doc.page_id)} pageIdList={onlineDocuments.map(doc => doc.page_id)}
onSelect={updateOnlineDocuments} onSelect={updateOnlineDocuments}
canPreview
onPreview={updateCurrentPage} onPreview={updateCurrentPage}
/> />
)} )}

View File

@ -14,7 +14,6 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-so
type OnlineDocumentsProps = { type OnlineDocumentsProps = {
pageIdList?: string[] pageIdList?: string[]
onSelect: (selectedPages: NotionPage[]) => void onSelect: (selectedPages: NotionPage[]) => void
canPreview?: boolean
previewPageId?: string previewPageId?: string
onPreview?: (selectedPage: NotionPage) => void onPreview?: (selectedPage: NotionPage) => void
isInPipeline?: boolean isInPipeline?: boolean
@ -25,7 +24,6 @@ type OnlineDocumentsProps = {
const OnlineDocuments = ({ const OnlineDocuments = ({
pageIdList, pageIdList,
onSelect, onSelect,
canPreview,
previewPageId, previewPageId,
onPreview, onPreview,
isInPipeline = false, isInPipeline = false,
@ -97,19 +95,22 @@ const OnlineDocuments = ({
const handleSearchValueChange = useCallback((value: string) => { const handleSearchValueChange = useCallback((value: string) => {
setSearchValue(value) setSearchValue(value)
}, []) }, [])
const handleSelectWorkspace = useCallback((workspaceId: string) => { const handleSelectWorkspace = useCallback((workspaceId: string) => {
setCurrentWorkspaceId(workspaceId) setCurrentWorkspaceId(workspaceId)
}, []) }, [])
const handleSelectPages = (newSelectedPagesId: Set<string>) => {
const handleSelectPages = useCallback((newSelectedPagesId: Set<string>) => {
const selectedPages = Array.from(newSelectedPagesId).map(pageId => PagesMapAndSelectedPagesId[0][pageId]) const selectedPages = Array.from(newSelectedPagesId).map(pageId => PagesMapAndSelectedPagesId[0][pageId])
setSelectedPagesId(new Set(Array.from(newSelectedPagesId))) setSelectedPagesId(new Set(Array.from(newSelectedPagesId)))
onSelect(selectedPages) onSelect(selectedPages)
} }, [onSelect, PagesMapAndSelectedPagesId])
const handlePreviewPage = (previewPageId: string) => {
const handlePreviewPage = useCallback((previewPageId: string) => {
if (onPreview) if (onPreview)
onPreview(PagesMapAndSelectedPagesId[0][previewPageId]) onPreview(PagesMapAndSelectedPagesId[0][previewPageId])
} }, [PagesMapAndSelectedPagesId, onPreview])
useEffect(() => { useEffect(() => {
setCurrentWorkspaceId(firstWorkspaceId) setCurrentWorkspaceId(firstWorkspaceId)
@ -154,9 +155,10 @@ const OnlineDocuments = ({
list={currentWorkspace?.pages || []} list={currentWorkspace?.pages || []}
pagesMap={PagesMapAndSelectedPagesId[0]} pagesMap={PagesMapAndSelectedPagesId[0]}
onSelect={handleSelectPages} onSelect={handleSelectPages}
canPreview={canPreview} canPreview={!isInPipeline}
previewPageId={previewPageId} previewPageId={previewPageId}
onPreview={handlePreviewPage} onPreview={handlePreviewPage}
isMultipleChoice={!isInPipeline}
/> />
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
import { useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { FixedSizeList as List } from 'react-window' import { FixedSizeList as List } from 'react-window'
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common' import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
@ -15,6 +15,7 @@ type PageSelectorProps = {
canPreview?: boolean canPreview?: boolean
previewPageId?: string previewPageId?: string
onPreview?: (selectedPageId: string) => void onPreview?: (selectedPageId: string) => void
isMultipleChoice?: boolean
} }
export type NotionPageTreeItem = { export type NotionPageTreeItem = {
@ -41,11 +42,14 @@ const PageSelector = ({
canPreview = true, canPreview = true,
previewPageId, previewPageId,
onPreview, onPreview,
isMultipleChoice = true,
}: PageSelectorProps) => { }: PageSelectorProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const [prevDataList, setPrevDataList] = useState(list) const [prevDataList, setPrevDataList] = useState(list)
const [dataList, setDataList] = useState<NotionPageItem[]>([]) const [dataList, setDataList] = useState<NotionPageItem[]>([])
const [localPreviewPageId, setLocalPreviewPageId] = useState('') const [localPreviewPageId, setLocalPreviewPageId] = useState('')
useEffect(() => {
if (prevDataList !== list) { if (prevDataList !== list) {
setPrevDataList(list) setPrevDataList(list)
setDataList(list.filter(item => item.parent_id === 'root' || !pagesMap[item.parent_id]).map((item) => { setDataList(list.filter(item => item.parent_id === 'root' || !pagesMap[item.parent_id]).map((item) => {
@ -56,6 +60,8 @@ const PageSelector = ({
} }
})) }))
} }
}, [prevDataList, list, pagesMap])
const searchDataList = list.filter((item) => { const searchDataList = list.filter((item) => {
return item.page_name.includes(searchValue) return item.page_name.includes(searchValue)
}).map((item) => { }).map((item) => {
@ -79,7 +85,7 @@ const PageSelector = ({
}, {}) }, {})
}, [list, pagesMap]) }, [list, pagesMap])
const handleToggle = (index: number) => { const handleToggle = useCallback((index: number) => {
const current = dataList[index] const current = dataList[index]
const pageId = current.page_id const pageId = current.page_id
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId] const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId]
@ -105,16 +111,16 @@ const PageSelector = ({
...dataList.slice(index + 1)] ...dataList.slice(index + 1)]
} }
setDataList(newDataList) setDataList(newDataList)
} }, [dataList, listMapWithChildrenAndDescendants, pagesMap])
const handleCheck = useCallback((index: number) => {
const copyValue = new Set([...value]) const copyValue = new Set([...value])
const handleCheck = (index: number) => {
const current = currentDataList[index] const current = currentDataList[index]
const pageId = current.page_id const pageId = current.page_id
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId] const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId]
if (copyValue.has(pageId)) { if (copyValue.has(pageId)) {
if (!searchValue) { if (!searchValue && isMultipleChoice) {
for (const item of currentWithChildrenAndDescendants.descendants) for (const item of currentWithChildrenAndDescendants.descendants)
copyValue.delete(item) copyValue.delete(item)
} }
@ -122,18 +128,24 @@ const PageSelector = ({
copyValue.delete(pageId) copyValue.delete(pageId)
} }
else { else {
if (!searchValue) { if (!searchValue && isMultipleChoice) {
for (const item of currentWithChildrenAndDescendants.descendants) for (const item of currentWithChildrenAndDescendants.descendants)
copyValue.add(item) copyValue.add(item)
} }
// Single choice mode, clear previous selection
if (!isMultipleChoice && copyValue.size > 0) {
copyValue.clear()
copyValue.add(pageId) copyValue.add(pageId)
} }
else {
copyValue.add(pageId)
}
}
onSelect(new Set([...copyValue])) onSelect(new Set([...copyValue]))
} }, [currentDataList, isMultipleChoice, listMapWithChildrenAndDescendants, onSelect, searchValue, value])
const handlePreview = (index: number) => { const handlePreview = useCallback((index: number) => {
const current = currentDataList[index] const current = currentDataList[index]
const pageId = current.page_id const pageId = current.page_id
@ -141,7 +153,7 @@ const PageSelector = ({
if (onPreview) if (onPreview)
onPreview(pageId) onPreview(pageId)
} }, [currentDataList, onPreview])
if (!currentDataList.length) { if (!currentDataList.length) {
return ( return (
@ -171,6 +183,7 @@ const PageSelector = ({
searchValue, searchValue,
previewPageId: currentPreviewPageId, previewPageId: currentPreviewPageId,
pagesMap, pagesMap,
isMultipleChoice,
}} }}
> >
{Item} {Item}

View File

@ -7,6 +7,7 @@ import Checkbox from '@/app/components/base/checkbox'
import NotionIcon from '@/app/components/base/notion-icon' import NotionIcon from '@/app/components/base/notion-icon'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common' import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
import Radio from '@/app/components/base/radio/ui'
type NotionPageTreeItem = { type NotionPageTreeItem = {
children: Set<string> children: Set<string>
@ -34,6 +35,7 @@ const Item = ({ index, style, data }: ListChildComponentProps<{
searchValue: string searchValue: string
previewPageId: string previewPageId: string
pagesMap: DataSourceNotionPageMap pagesMap: DataSourceNotionPageMap
isMultipleChoice?: boolean
}>) => { }>) => {
const { t } = useTranslation() const { t } = useTranslation()
const { const {
@ -48,6 +50,7 @@ const Item = ({ index, style, data }: ListChildComponentProps<{
searchValue, searchValue,
previewPageId, previewPageId,
pagesMap, pagesMap,
isMultipleChoice,
} = data } = data
const current = dataList[index] const current = dataList[index]
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[current.page_id] const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[current.page_id]
@ -88,16 +91,24 @@ const Item = ({ index, style, data }: ListChildComponentProps<{
previewPageId === current.page_id && 'bg-state-base-hover')} previewPageId === current.page_id && 'bg-state-base-hover')}
style={{ ...style, top: style.top as number + 8, left: 8, right: 8, width: 'calc(100% - 16px)' }} style={{ ...style, top: style.top as number + 8, left: 8, right: 8, width: 'calc(100% - 16px)' }}
> >
{isMultipleChoice ? (
<Checkbox <Checkbox
className='mr-2 shrink-0' className='mr-2 shrink-0'
checked={checkedIds.has(current.page_id)} checked={checkedIds.has(current.page_id)}
disabled={disabled} disabled={disabled}
onCheck={() => { onCheck={() => {
if (disabled) handleCheck(index)
return }}
/>) : (
<Radio
className='mr-2 shrink-0'
isChecked={checkedIds.has(current.page_id)}
disabled={disabled}
onCheck={() => {
handleCheck(index) handleCheck(index)
}} }}
/> />
)}
{!searchValue && renderArrow()} {!searchValue && renderArrow()}
<NotionIcon <NotionIcon
className='mr-1 shrink-0' className='mr-1 shrink-0'

View File

@ -169,7 +169,7 @@ const WebsiteCrawl = ({
usedTime={Number.parseFloat(crawlResult?.time_consuming as string) || 0} usedTime={Number.parseFloat(crawlResult?.time_consuming as string) || 0}
previewIndex={previewIndex} previewIndex={previewIndex}
onPreview={onPreview} onPreview={onPreview}
isMultipleChoice={isInPipeline} // only support single choice in test run isMultipleChoice={!isInPipeline} // only support single choice in test run
/> />
)} )}
</div> </div>