From e0cdbdb0e7e7961c824cffdf1850cc657a82abd0 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 8 Jun 2023 12:47:04 +0800 Subject: [PATCH] More work on multiple selection mode --- .../patterns/LibraryCards/CardTypes.tsx | 6 +- .../patterns/LibraryCards/LibraryGridCard.tsx | 8 +- .../patterns/LibraryCards/LibraryListCard.tsx | 8 +- .../components/templates/PrimaryDropdown.tsx | 18 +-- .../templates/homeFeed/HomeFeedContainer.tsx | 87 ++++++++++++- .../templates/homeFeed/LibraryFilterMenu.tsx | 4 +- .../templates/homeFeed/LibraryHeader.tsx | 119 +++++++++++++----- 7 files changed, 194 insertions(+), 56 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/CardTypes.tsx b/packages/web/components/patterns/LibraryCards/CardTypes.tsx index 278748d10..2833d724d 100644 --- a/packages/web/components/patterns/LibraryCards/CardTypes.tsx +++ b/packages/web/components/patterns/LibraryCards/CardTypes.tsx @@ -19,8 +19,12 @@ export type LinkedItemCardProps = { item: LibraryItemNode layout: LayoutType viewer: UserBasicData - inMultiSelect: boolean + handleAction: (action: LinkedItemCardAction) => void + inMultiSelect: boolean + isChecked: boolean + setIsChecked: (itemId: string, set: boolean) => void + isHovered?: boolean } diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 6484079ef..9775ebbe4 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -105,13 +105,11 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => { const [menuOpen, setMenuOpen] = useState(false) - const [isChecked, setIsChecked] = useState(false) - const originText = siteName(props.item.originalArticleUrl, props.item.url) const handleCheckChanged = useCallback(() => { - setIsChecked(!isChecked) - }, [isChecked]) + props.setIsChecked(props.item.id, !props.isChecked) + }, [props.isChecked]) return ( <> @@ -126,7 +124,7 @@ const LibraryGridCardContent = (props: LinkedItemCardProps): JSX.Element => { {props.inMultiSelect ? ( diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index ff57c9c2f..15330fa6e 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -74,13 +74,11 @@ export function LibraryListCardContent( props: LinkedItemCardProps ): JSX.Element { const [menuOpen, setMenuOpen] = useState(false) - const [isChecked, setIsChecked] = useState(false) - const originText = siteName(props.item.originalArticleUrl, props.item.url) const handleCheckChanged = useCallback(() => { - setIsChecked(!isChecked) - }, [isChecked]) + props.setIsChecked(props.item.id, !props.isChecked) + }, [props.isChecked]) return ( <> @@ -89,7 +87,7 @@ export function LibraryListCardContent( {props.inMultiSelect ? ( diff --git a/packages/web/components/templates/PrimaryDropdown.tsx b/packages/web/components/templates/PrimaryDropdown.tsx index bb2fdacd4..b000a806b 100644 --- a/packages/web/components/templates/PrimaryDropdown.tsx +++ b/packages/web/components/templates/PrimaryDropdown.tsx @@ -24,6 +24,7 @@ type PrimaryDropdownProps = { layout?: LayoutType updateLayout?: (layout: LayoutType) => void + showAddLinkModal?: () => void startSelectMultiple?: () => void } @@ -37,7 +38,6 @@ export type HeaderDropdownAction = | 'navigate-to-integrations' | 'increaseFontSize' | 'decreaseFontSize' - | 'begin-select-multiple' | 'logout' export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element { @@ -65,11 +65,6 @@ export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element { case 'navigate-to-integrations': router.push('/settings/integrations') break - case 'begin-select-multiple': - if (props.startSelectMultiple) { - props.startSelectMultiple() - } - break case 'logout': document.dispatchEvent(new Event('logout')) break @@ -161,12 +156,21 @@ export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element { onSelect={() => headerDropdownActionHandler('navigate-to-labels')} title="Labels" /> + {props.startSelectMultiple && ( headerDropdownActionHandler('begin-select-multiple')} + onSelect={() => + props.startSelectMultiple && props.startSelectMultiple() + } title="Select Multiple" /> )} + {props.showAddLinkModal && ( + props.showAddLinkModal && props.showAddLinkModal()} + title="Add Link" + /> + )} { @@ -521,11 +522,58 @@ export function HomeFeedContainer(): JSX.Element { ) useFetchMore(handleFetchMore) + const [checkedItems, setCheckedItems] = useState([]) + const [multiSelectMode, setMultiSelectMode] = useState('off') + + const setIsChecked = useCallback( + (itemId: string, set: boolean) => { + if (set && checkedItems.indexOf(itemId) === -1) { + checkedItems.push(itemId) + setCheckedItems([...checkedItems]) + } else if (!set && checkedItems.indexOf(itemId) !== -1) { + setCheckedItems(checkedItems.splice(checkedItems.indexOf(itemId), 1)) + } + }, + [checkedItems] + ) + + useEffect(() => { + console.log('switching on multiselect mode: ', multiSelectMode) + switch (multiSelectMode) { + case 'off': + case 'none': + setCheckedItems([]) + break + case 'some': + break + case 'search': + case 'visible': + const allIds = ( + itemsPages?.flatMap((ad) => { + return ad.search.edges + }) || [] + ).map((item) => item.node.id) + setCheckedItems(allIds) + break + } + }, [multiSelectMode]) + + const itemIsChecked = useCallback( + (itemId: string) => { + return checkedItems.indexOf(itemId) !== -1 + }, + [checkedItems] + ) + return ( ) } @@ -609,6 +662,12 @@ type HomeFeedContentProps = { action: LinkedItemCardAction, item: LibraryItem | undefined ) => Promise + + multiSelectMode: MultiSelectMode + setIsChecked: (itemId: string, set: boolean) => void + itemIsChecked: (itemId: string) => boolean + setMultiSelectMode: (mode: MultiSelectMode) => void + numItemsSelected: number } function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { @@ -627,7 +686,8 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { ) const [showFilterMenu, setShowFilterMenu] = useState(false) - const [inMultiSelect, setInMultiSelect] = useState(false) + + console.log('props.multiSelectMode: ', props.multiSelectMode) return ( { - console.log('searching with searchQuery: ', searchQuery) props.applySearchQuery(searchQuery) }} showFilterMenu={showFilterMenu} setShowFilterMenu={setShowFilterMenu} - inMultiSelect={inMultiSelect} - setInMultiSelect={setInMultiSelect} + multiSelectMode={props.multiSelectMode} + setMultiSelectMode={props.setMultiSelectMode} + numItemsSelected={props.numItemsSelected} + showAddLinkModal={() => props.setShowAddLinkModal(true)} /> )} @@ -690,6 +752,9 @@ type LibraryItemsLayoutProps = { layout: LayoutType viewer?: UserBasicData inMultiSelect: boolean + + isChecked: (itemId: string) => boolean + setIsChecked: (itemId: string, set: boolean) => void } & HomeFeedContentProps function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element { @@ -749,6 +814,8 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element { items={props.items} layout={props.layout} viewer={props.viewer} + isChecked={props.isChecked} + setIsChecked={props.setIsChecked} gridContainerRef={props.gridContainerRef} setShowEditTitleModal={props.setShowEditTitleModal} setLinkToEdit={props.setLinkToEdit} @@ -811,6 +878,10 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element { item={props.linkToRemove?.node} viewer={props.viewer} layout="GRID_LAYOUT" + inMultiSelect={false} + isChecked={false} + // eslint-disable-next-line @typescript-eslint/no-empty-function + setIsChecked={() => {}} // eslint-disable-next-line @typescript-eslint/no-empty-function handleAction={() => {}} /> @@ -879,6 +950,8 @@ type LibraryItemsProps = { setShowRemoveLinkConfirmation: (show: true) => void inMultiSelect: boolean + isChecked: (itemId: string) => boolean + setIsChecked: (itemId: string, set: boolean) => void actionHandler: ( action: LinkedItemCardAction, @@ -963,6 +1036,8 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element { layout={props.layout} item={linkedItem.node} viewer={props.viewer} + isChecked={props.isChecked(linkedItem.node.id)} + setIsChecked={props.setIsChecked} inMultiSelect={props.inMultiSelect} handleAction={(action: LinkedItemCardAction) => { if (action === 'delete') { diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx index a682b87b6..7cc08ee7a 100644 --- a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx +++ b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx @@ -67,10 +67,10 @@ export function LibraryFilterMenu(props: LibraryFilterMenuProps): JSX.Element { - + {/* props.setShowAddLinkModal(true)} - /> + /> */} {/* This spacer pushes library content to the right of diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 46f859400..f1f2c07fe 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -29,6 +29,8 @@ import { import { CardCheckbox } from '../../patterns/LibraryCards/LibraryCardStyles' import { Dropdown, DropdownOption } from '../../elements/DropdownElements' +export type MultiSelectMode = 'off' | 'none' | 'some' | 'visible' | 'search' + type LibraryHeaderProps = { layout: LayoutType updateLayout: (layout: LayoutType) => void @@ -39,21 +41,26 @@ type LibraryHeaderProps = { showFilterMenu: boolean setShowFilterMenu: (show: boolean) => void - inMultiSelect: boolean - setInMultiSelect: (set: boolean) => void + showAddLinkModal: () => void + + numItemsSelected: number + multiSelectMode: MultiSelectMode + setMultiSelectMode: (mode: MultiSelectMode) => void } export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { - const [isScrolled, setIsScrolled] = useState(props.inMultiSelect) + const [showBackground, setShowBackground] = useState( + props.multiSelectMode !== 'off' + ) useEffect(() => { - if (window.scrollY > 5 || props.inMultiSelect) { - setIsScrolled(true) + if (window.scrollY > 5 || props.multiSelectMode != 'off') { + setShowBackground(true) } }) useScrollWatcher((changeset: ScrollOffsetChangeset) => { - setIsScrolled(window.scrollY > 5) + setShowBackground(window.scrollY > 5) }, 0) return ( @@ -68,7 +75,7 @@ export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { zIndex: 5, position: 'fixed', height: HEADER_HEIGHT, - bg: isScrolled ? '$thBackground' : 'transparent', + bg: showBackground ? '$thBackground' : 'transparent', '@mdDown': { left: '0px', right: '0', @@ -100,13 +107,13 @@ function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element { }, }} > - {/* */} - {/* */} ) @@ -148,8 +155,10 @@ function SmallHeaderLayout(props: LibraryHeaderProps): JSX.Element { layout={props.layout} updateLayout={props.updateLayout} setShowInlineSearch={setShowInlineSearch} - inMultiSelect={props.inMultiSelect} - setInMultiSelect={props.setInMultiSelect} + numItemsSelected={props.numItemsSelected} + multiSelectMode={props.multiSelectMode} + setMultiSelectMode={props.setMultiSelectMode} + showAddLinkModal={props.showAddLinkModal} /> )} @@ -354,8 +363,11 @@ type ControlButtonBoxProps = { updateLayout: (layout: LayoutType) => void setShowInlineSearch?: (show: boolean) => void - inMultiSelect: boolean - setInMultiSelect: (set: boolean) => void + showAddLinkModal: () => void + + numItemsSelected: number + multiSelectMode: MultiSelectMode + setMultiSelectMode: (mode: MultiSelectMode) => void } function MultiSelectControlButtonBox( @@ -420,7 +432,7 @@ function MultiSelectControlButtonBox( + + + {props.numItemsSelected} selected + )} - {props.inMultiSelect ? ( + {props.multiSelectMode !== 'off' ? ( <> - + { + // setIsChecked(false) + // props.setInMultiSelect(set) + // }} + /> ) : ( @@ -586,7 +645,7 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element { layout={props.layout} updateLayout={props.updateLayout} startSelectMultiple={() => { - props.setInMultiSelect(true) + props.setMultiSelectMode('none') }} />