import React, { ReactElement, ReactNode } from 'react' import { Eq } from 'fp-ts/lib/Eq' import { constVoid } from 'fp-ts/lib/function' import * as RA from 'fp-ts/lib/ReadonlyArray' import { Colors, flexFlow, getColor } from '@monorail/helpers/exports' import styled, { css } from '@monorail/helpers/styled-components' import { Button } from '@monorail/visualComponents/buttons/Button' import { ButtonDisplay, ButtonSize, } from '@monorail/visualComponents/buttons/buttonTypes' import { IconButton } from '@monorail/visualComponents/buttons/IconButton' import { renderDefaultEmptyState, renderDefaultOptionsList, } from '@monorail/visualComponents/cardList/CardList.helpers' import { useCardListForEditing } from '@monorail/visualComponents/cardList/CardList.hooks' import { CardListSearchSection } from '@monorail/visualComponents/cardList/CardListSearchSection' import { RenderChoiceProps } from '@monorail/visualComponents/cardList/OptionsList' import { BBCardBackground } from '@monorail/visualComponents/cards/Cards' import { Header, HeaderTitle } from '@monorail/visualComponents/header/Header' import { ScrollAnimation } from '@monorail/visualComponents/layout/ScrollAnimation' const ListContainer = styled(ScrollAnimation)` padding-bottom: 4px; ` const IconHeaderContainer = styled.div` ${flexFlow('row')} align-items: center; ` export type CardListCommonProps = { readonly selectedItems: ReadonlyArray /** * Title to show in Header when in view mode */ readonly headerTitle: ReactNode /** * Render an item as a row in the list of selected items */ readonly renderSelectedItem: (item: A) => ReactElement /** * Render an empty state for the card content section */ readonly renderEmptyState?: (params: { openSearch: () => void closeSearch: () => void isSearchOpen: boolean }) => ReactElement } export type CardListEditProps = { readonly mode: 'edit' readonly allItems: ReadonlyArray readonly onChangeSelectedItems: (value: ReadonlyArray) => void /** * Title to show in Header when adding items (will default to `headerTitle` * prop if not provided) */ readonly headerTitleAddingItemsMode?: ReactNode /** * Optional placeholder for search input */ readonly searchPlaceholder?: string /** * Convert an item to an array of strings that can be matched on while * searching (defaults to using `primaryText` from `toListItem` prop) * NB: Search is *not* case-sensitive */ readonly toSearchableStrings?: (item: A) => ReadonlyArray /** * An `Eq` instance for distinguishing the subset of selected items from the * list of all items */ readonly eq: Eq readonly toListItem: ( item: A, ) => { id: string; primaryText: string; secondaryText?: string } /** * Given the list of items converted to `RenderChoiceProps`, render as an * options list (defaults to `OptionsList` component) */ readonly renderOptionsList?: ( options: ReadonlyArray>, ) => ReactElement } type CardListViewProps = { readonly mode: 'view' } export type CardListProps = CardListCommonProps & (CardListEditProps | CardListViewProps) const CardListContentForEditing = ( props: CardListCommonProps & CardListEditProps, ) => { const { headerTitle, headerTitleAddingItemsMode = headerTitle, renderEmptyState = renderDefaultEmptyState, renderOptionsList = renderDefaultOptionsList, renderSelectedItem, searchPlaceholder, selectedItems, } = props const { closeSearch, handleCancel, handleConfirm, isButtonDisabled, isSearchOpen, openSearch, options, searchValue, setSearchValue, } = useCardListForEditing(props) return ( <>
{headerTitleAddingItemsMode} ) : ( headerTitle ) } actions={ isSearchOpen ? ( ) : ( ) } /> {isSearchOpen ? ( ) : ( <> )} {isSearchOpen ? renderOptionsList(options) : !RA.isEmpty(selectedItems) ? RA.map(renderSelectedItem)(selectedItems) : renderEmptyState({ isSearchOpen, openSearch, closeSearch })} ) } const CardListContentViewOnly = ({ headerTitle, renderSelectedItem, selectedItems, renderEmptyState = renderDefaultEmptyState, }: CardListCommonProps & CardListViewProps) => ( <>
{!RA.isEmpty(selectedItems) ? RA.map(renderSelectedItem)(selectedItems) : renderEmptyState({ openSearch: constVoid, closeSearch: constVoid, isSearchOpen: false, })} ) export const CardListContent = (props: CardListProps) => props.mode === 'edit' ? ( ) : ( ) /** * @NOTE - Pete Murphy 2020-09-22 - Keeping this "wrapper" component separate in * case caller wants to, e.g., render a loading spinner inside while waiting for * `CardListProps` to resolve: * * @example * ```tsx * * {remoteLoadingFold(itemsRemoteData, items => * * // ... * /> * )} * * ``` */ export const CardListWrapper = styled(BBCardBackground)` height: 240px; width: 256px; ` export const CardList = (props: CardListProps) => ( )