import * as React from 'react'
import * as types from 'notion-types'
import {
getBlockCollectionId,
getBlockParentPage,
getTextContent
} from 'notion-utils'
import { useLocalStorage, useWindowSize } from 'react-use'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { CollectionRow } from './collection-row'
import { CollectionViewIcon } from '../icons/collection-view-icon'
import CheckIcon from '../icons/check'
import { ChevronDownIcon } from '../icons/chevron-down-icon'
import { CollectionView } from './collection-view'
import { PropertyImplMemo } from './property'
import { PageIcon } from '../components/page-icon'
import {
NotionContext,
NotionContextProvider,
useNotionContext
} from '../context'
import { cs } from '../utils'
const isServer = typeof window === 'undefined'
export const Collection: React.FC<{
block:
| types.CollectionViewBlock
| types.CollectionViewPageBlock
| types.PageBlock
className?: string
ctx: NotionContext
}> = ({ block, className, ctx }) => {
/**
* NOTE: there is a weird side effect of us using multiple bundles for
* collections, where `useNotionContext` returns a *different* context than for
* the main bundle.
*
* This is due to `React.createContext` being called in each bundle which includes
* `../context.ts`.
*
* To circumvent this issue, we're passing the context value directly to
* `Collection` so all children have the correct context values.
*/
// console.log('Collection', Object.keys(recordMap.block).length)
const context: NotionContext = React.useMemo(
() => ({
...ctx
}),
[ctx]
)
if (block.type === 'page') {
if (block.parent_table !== 'collection') {
return null
}
return (
)
} else {
return (
)
}
}
const CollectionViewBlock: React.FC<{
block: types.CollectionViewBlock | types.CollectionViewPageBlock
className?: string
}> = ({ block, className }) => {
const { recordMap, showCollectionViewDropdown } = useNotionContext()
const { view_ids: viewIds } = block
const collectionId = getBlockCollectionId(block, recordMap)
const [isMounted, setIsMounted] = React.useState(false)
React.useEffect(() => {
setIsMounted(true)
}, [])
const defaultCollectionViewId = viewIds[0]
const [collectionState, setCollectionState] = useLocalStorage(block.id, {
collectionViewId: defaultCollectionViewId
})
const collectionViewId =
(isMounted &&
viewIds.find((id) => id === collectionState.collectionViewId)) ||
defaultCollectionViewId
const onChangeView = React.useCallback(
(collectionViewId: string) => {
console.log('change collection view', collectionViewId)
setCollectionState({
...collectionState,
collectionViewId
})
},
[collectionState, setCollectionState]
)
let { width: windowWidth } = useWindowSize()
if (isServer) {
windowWidth = 1024
}
const collection = recordMap.collection[collectionId]?.value
const collectionView = recordMap.collection_view[collectionViewId]?.value
const collectionData =
recordMap.collection_query[collectionId]?.[collectionViewId]
const parentPage = getBlockParentPage(block, recordMap)
const { style, width, padding } = React.useMemo(() => {
const style: React.CSSProperties = {}
if (collectionView?.type !== 'table' && collectionView?.type !== 'board') {
return {
style,
width: 0,
padding: 0
}
}
const width = windowWidth
// TODO: customize for mobile?
const maxNotionBodyWidth = 708
let notionBodyWidth = maxNotionBodyWidth
if (parentPage?.format?.page_full_width) {
notionBodyWidth = (width - 2 * Math.min(96, width * 0.08)) | 0
} else {
notionBodyWidth =
width < maxNotionBodyWidth
? (width - width * 0.02) | 0 // 2vw
: maxNotionBodyWidth
}
const padding =
isServer && !isMounted ? 96 : ((width - notionBodyWidth) / 2) | 0
style.paddingLeft = padding
style.paddingRight = padding
return {
style,
width,
padding
}
}, [windowWidth, parentPage, collectionView?.type, isMounted])
// console.log({
// width,
// padding
// })
if (!(collection && collectionView && collectionData)) {
console.warn('skipping missing collection view for block', block.id, {
collectionId,
collectionViewId,
collectionView,
collectionData,
recordMap
})
return null
}
const title = getTextContent(collection.name).trim()
if (collection.icon) {
block.format = {
...block.format,
page_icon: collection.icon
}
}
return (
{title && (
)}
{viewIds.length > 1 && showCollectionViewDropdown && (
)}
)
}
const CollectionViewDropdownMenu: React.FC<{
collectionViewId: string
collectionView: types.CollectionView
viewIds: string[]
onChangeView: (viewId: string) => unknown
}> = ({ collectionViewId, collectionView, viewIds, onChangeView }) => {
const { recordMap } = useNotionContext()
return (
{viewIds.map((viewId) => (
{collectionViewId === viewId && (
)}
))}
)
}
const CollectionViewColumnDesc: React.FC<{
collectionView: types.CollectionView
className?: string
children?: React.ReactNode
}> = ({ collectionView, className, children, ...rest }) => {
const { type } = collectionView
const name =
collectionView.name || `${type[0].toUpperCase()}${type.slice(1)} view`
return (
{name}
{children}
)
}
export { PropertyImplMemo as Property }