Commit 1ee00cba authored by StyleZhang's avatar StyleZhang

fix: notion-page-selector

parent 6b884ab2
...@@ -16,7 +16,7 @@ const NotionIcon = ({ ...@@ -16,7 +16,7 @@ const NotionIcon = ({
}: NotionIconProps) => { }: NotionIconProps) => {
if (type === 'workspace') { if (type === 'workspace') {
if (src) { if (src) {
if (src.includes('https://')) { if (src.startsWith('https://') || src.startsWith('http://')) {
return ( return (
<img <img
alt='workspace icon' alt='workspace icon'
...@@ -35,7 +35,7 @@ const NotionIcon = ({ ...@@ -35,7 +35,7 @@ const NotionIcon = ({
} }
if (src) { if (src) {
if (src.includes('https://')) { if (src.startsWith('https://') || src.startsWith('http://')) {
return ( return (
<img <img
alt='page icon' alt='page icon'
......
...@@ -10,12 +10,14 @@ import AccountSetting from '@/app/components/header/account-setting' ...@@ -10,12 +10,14 @@ import AccountSetting from '@/app/components/header/account-setting'
import type { DataSourceNotionPage } from '@/models/common' import type { DataSourceNotionPage } from '@/models/common'
type NotionPageSelectorProps = { type NotionPageSelectorProps = {
value?: string[]
onSelect: (selectedPages: (DataSourceNotionPage & { workspace_id: string })[]) => void onSelect: (selectedPages: (DataSourceNotionPage & { workspace_id: string })[]) => void
canPreview?: boolean canPreview?: boolean
onPreview?: (selectedPage: DataSourceNotionPage & { workspace_id: string }) => void onPreview?: (selectedPage: DataSourceNotionPage & { workspace_id: string }) => void
} }
const NotionPageSelector = ({ const NotionPageSelector = ({
value,
onSelect, onSelect,
canPreview, canPreview,
onPreview, onPreview,
...@@ -77,6 +79,9 @@ const NotionPageSelector = ({ ...@@ -77,6 +79,9 @@ const NotionPageSelector = ({
{ {
currentWorkspace?.source_info.pages.length && ( currentWorkspace?.source_info.pages.length && (
<PageSelector <PageSelector
key={currentWorkspaceId}
value={value}
searchValue={searchValue}
list={currentWorkspace?.source_info.pages} list={currentWorkspace?.source_info.pages}
onSelect={handleSelecPages} onSelect={handleSelecPages}
canPreview={canPreview} canPreview={canPreview}
......
import { memo, useState } from 'react' import { memo, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { FixedSizeList as List, areEqual } from 'react-window' import { FixedSizeList as List, areEqual } from 'react-window'
import type { ListChildComponentProps } from 'react-window' import type { ListChildComponentProps } from 'react-window'
...@@ -9,6 +9,8 @@ import s from './index.module.css' ...@@ -9,6 +9,8 @@ import s from './index.module.css'
import type { DataSourceNotionPage } from '@/models/common' import type { DataSourceNotionPage } from '@/models/common'
type PageSelectorProps = { type PageSelectorProps = {
value?: string[]
searchValue: string
list: DataSourceNotionPage[] list: DataSourceNotionPage[]
onSelect: (selectedPages: DataSourceNotionPage[]) => void onSelect: (selectedPages: DataSourceNotionPage[]) => void
canPreview?: boolean canPreview?: boolean
...@@ -19,6 +21,7 @@ type NotionPageTreeItem = { ...@@ -19,6 +21,7 @@ type NotionPageTreeItem = {
children: Set<string> children: Set<string>
descendants: Set<string> descendants: Set<string>
deepth: number deepth: number
ancestors: string[]
} & DataSourceNotionPage } & DataSourceNotionPage
type NotionPageTreeMap = Record<string, NotionPageTreeItem> type NotionPageTreeMap = Record<string, NotionPageTreeItem>
type NotionPageItem = { type NotionPageItem = {
...@@ -45,6 +48,7 @@ const recursivePushInParentDescendants = ( ...@@ -45,6 +48,7 @@ const recursivePushInParentDescendants = (
children, children,
descendants, descendants,
deepth: 0, deepth: 0,
ancestors: [],
} }
} }
else { else {
...@@ -53,6 +57,7 @@ const recursivePushInParentDescendants = ( ...@@ -53,6 +57,7 @@ const recursivePushInParentDescendants = (
listTreeMap[parentId].descendants.add(leafItem.page_id) listTreeMap[parentId].descendants.add(leafItem.page_id)
} }
leafItem.deepth++ leafItem.deepth++
leafItem.ancestors.unshift(listTreeMap[parentId].page_name)
if (listTreeMap[parentId].parent_id !== 'root') if (listTreeMap[parentId].parent_id !== 'root')
recursivePushInParentDescendants(listMap, listTreeMap, listTreeMap[parentId], leafItem) recursivePushInParentDescendants(listMap, listTreeMap, listTreeMap[parentId], leafItem)
...@@ -66,10 +71,16 @@ const Item = memo(({ index, style, data }: ListChildComponentProps<{ ...@@ -66,10 +71,16 @@ const Item = memo(({ index, style, data }: ListChildComponentProps<{
handleCheck: (index: number) => void handleCheck: (index: number) => void
canPreview?: boolean canPreview?: boolean
handlePreview: (index: number) => void handlePreview: (index: number) => void
listMapWithChildrenAndDescendants: NotionPageTreeMap
searchValue: string
}>) => { }>) => {
const { t } = useTranslation() const { t } = useTranslation()
const { dataList, handleToggle, checkedIds, handleCheck, canPreview, handlePreview } = data const { dataList, handleToggle, checkedIds, handleCheck, canPreview, handlePreview, listMapWithChildrenAndDescendants, searchValue } = data
const current = dataList[index] const current = dataList[index]
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[current.page_id]
const hasChild = currentWithChildrenAndDescendants.descendants.size > 0
const ancestors = currentWithChildrenAndDescendants.ancestors
const breadCrumbs = ancestors.length ? [...ancestors, current.page_name] : [current.page_name]
let iconSrc let iconSrc
if (current.page_icon && current.page_icon.type === 'url') if (current.page_icon && current.page_icon.type === 'url')
...@@ -78,6 +89,20 @@ const Item = memo(({ index, style, data }: ListChildComponentProps<{ ...@@ -78,6 +89,20 @@ const Item = memo(({ index, style, data }: ListChildComponentProps<{
if (current.page_icon && current.page_icon.type === 'emoji') if (current.page_icon && current.page_icon.type === 'emoji')
iconSrc = current.page_icon.emoji iconSrc = current.page_icon.emoji
const renderArrow = () => {
return hasChild
? (
<div
className={cn(s.arrow, current.expand && s['arrow-expand'], 'shrink-0 mr-1 w-5 h-5 hover:bg-gray-200 rounded-md')}
style={{ marginLeft: current.deepth * 8 }}
onClick={() => handleToggle(index)}
/>
)
: (
<div className='shrink-0 mr-1 w-5 h-5' style={{ marginLeft: current.deepth * 8 }} />
)
}
return ( return (
<div <div
className='group flex items-center pl-2 pr-[2px] rounded-md hover:bg-gray-100 cursor-pointer' className='group flex items-center pl-2 pr-[2px] rounded-md hover:bg-gray-100 cursor-pointer'
...@@ -88,11 +113,7 @@ const Item = memo(({ index, style, data }: ListChildComponentProps<{ ...@@ -88,11 +113,7 @@ const Item = memo(({ index, style, data }: ListChildComponentProps<{
checked={checkedIds.has(current.page_id)} checked={checkedIds.has(current.page_id)}
onCheck={() => handleCheck(index)} onCheck={() => handleCheck(index)}
/> />
<div {!searchValue && renderArrow()}
className={cn(s.arrow, current.expand && s['arrow-expand'], 'shrink-0 mr-1 w-5 h-5 hover:bg-gray-200 rounded-md')}
style={{ marginLeft: current.deepth * 8 }}
onClick={() => handleToggle(index)}
/>
<NotionIcon <NotionIcon
className='shrink-0 mr-1' className='shrink-0 mr-1'
type='page' type='page'
...@@ -107,17 +128,29 @@ const Item = memo(({ index, style, data }: ListChildComponentProps<{ ...@@ -107,17 +128,29 @@ const Item = memo(({ index, style, data }: ListChildComponentProps<{
{ {
canPreview && ( canPreview && (
<div <div
className='shrink-0 hidden group-hover:flex items-center ml-4 px-2 h-6 rounded-md text-xs font-medium text-gray-700 cursor-pointer hover:bg-gray-50' className='shrink-0 hidden group-hover:flex items-center ml-1 px-2 h-6 rounded-md text-xs font-medium text-gray-700 cursor-pointer hover:bg-gray-50'
onClick={() => handlePreview(index)}> onClick={() => handlePreview(index)}>
{t('common.dataSource.notion.selector.preview')} {t('common.dataSource.notion.selector.preview')}
</div> </div>
) )
} }
{
searchValue && (
<div
className='shrink-0 ml-1 max-w-[120px] text-xs text-gray-400 truncate'
title={breadCrumbs.join(' / ')}
>
{breadCrumbs.join(' / ')}
</div>
)
}
</div> </div>
) )
}, areEqual) }, areEqual)
const PageSelector = ({ const PageSelector = ({
value,
searchValue,
list, list,
onSelect, onSelect,
canPreview = true, canPreview = true,
...@@ -132,7 +165,8 @@ const PageSelector = ({ ...@@ -132,7 +165,8 @@ const PageSelector = ({
} }
}), }),
) )
const [checkedIds, setCheckedIds] = useState<Set<string>>(new Set()) const [searchDataList, setSearchDataList] = useState<NotionPageItem[]>([])
const [checkedIds, setCheckedIds] = useState<Set<string>>(new Set(value || []))
const listMap = list.reduce((prev: NotionPageMap, next: DataSourceNotionPage) => { const listMap = list.reduce((prev: NotionPageMap, next: DataSourceNotionPage) => {
prev[next.page_id] = next prev[next.page_id] = next
...@@ -141,7 +175,7 @@ const PageSelector = ({ ...@@ -141,7 +175,7 @@ const PageSelector = ({
const listMapWithChildrenAndDescendants = list.reduce((prev: NotionPageTreeMap, next: DataSourceNotionPage) => { const listMapWithChildrenAndDescendants = list.reduce((prev: NotionPageTreeMap, next: DataSourceNotionPage) => {
const pageId = next.page_id const pageId = next.page_id
if (!prev[pageId]) if (!prev[pageId])
prev[pageId] = { ...next, children: new Set(), descendants: new Set(), deepth: 0 } prev[pageId] = { ...next, children: new Set(), descendants: new Set(), deepth: 0, ancestors: [] }
recursivePushInParentDescendants(listMap, prev, prev[pageId], prev[pageId]) recursivePushInParentDescendants(listMap, prev, prev[pageId], prev[pageId])
return prev return prev
...@@ -176,18 +210,24 @@ const PageSelector = ({ ...@@ -176,18 +210,24 @@ const PageSelector = ({
} }
const handleCheck = (index: number) => { const handleCheck = (index: number) => {
const current = dataList[index] const current = searchValue ? searchDataList[index] : dataList[index]
const pageId = current.page_id const pageId = current.page_id
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId] const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId]
if (checkedIds.has(pageId)) { if (checkedIds.has(pageId)) {
for (const item of currentWithChildrenAndDescendants.descendants) if (!searchValue) {
checkedIds.delete(item) for (const item of currentWithChildrenAndDescendants.descendants)
checkedIds.delete(item)
}
checkedIds.delete(pageId) checkedIds.delete(pageId)
} }
else { else {
for (const item of currentWithChildrenAndDescendants.descendants) if (!searchValue) {
checkedIds.add(item) for (const item of currentWithChildrenAndDescendants.descendants)
checkedIds.add(item)
}
checkedIds.add(pageId) checkedIds.add(pageId)
} }
...@@ -203,20 +243,35 @@ const PageSelector = ({ ...@@ -203,20 +243,35 @@ const PageSelector = ({
} }
} }
useEffect(() => {
setSearchDataList(list.filter((item) => {
return item.page_name.includes(searchValue)
}).map((item) => {
return {
...item,
expand: false,
deepth: 0,
}
}))
}, [searchValue])
return ( return (
<List <List
className='py-2' className='py-2'
height={296} height={296}
itemCount={dataList.length} itemCount={searchValue ? searchDataList.length : dataList.length}
itemSize={28} itemSize={28}
width='100%' width='100%'
itemKey={(index, data) => data.dataList[index].page_id}
itemData={{ itemData={{
dataList, dataList: searchValue ? searchDataList : dataList,
handleToggle, handleToggle,
checkedIds, checkedIds,
handleCheck, handleCheck,
canPreview, canPreview,
handlePreview, handlePreview,
listMapWithChildrenAndDescendants,
searchValue,
}} }}
> >
{Item} {Item}
......
...@@ -9,5 +9,7 @@ ...@@ -9,5 +9,7 @@
} }
.input-wrapper { .input-wrapper {
flex-basis: 200px;
width: 0;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
} }
\ No newline at end of file
...@@ -19,7 +19,7 @@ const SearchInput = ({ ...@@ -19,7 +19,7 @@ const SearchInput = ({
}, []) }, [])
return ( return (
<div className={cn(s['input-wrapper'], 'flex items-center px-2 w-50 h-7 border border-gray-300 rounded-md', `${value ? 'bg-white' : 'bg-gray-100'}`)}> <div className={cn(s['input-wrapper'], 'flex items-center px-2 h-7 border border-gray-300 rounded-md', `${value ? 'bg-white' : 'bg-gray-100'}`)}>
<div className={cn(s['search-icon'], 'mr-[6px] w-4 h-4')} /> <div className={cn(s['search-icon'], 'mr-[6px] w-4 h-4')} />
<input <input
className='grow text-[13px] bg-inherit border-0 outline-0 appearance-none' className='grow text-[13px] bg-inherit border-0 outline-0 appearance-none'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment