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 && (
{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 }