More work on multiple selection mode
This commit is contained in:
@ -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
|
||||
}
|
||||
|
||||
@ -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 ? (
|
||||
<SpanBox css={{ marginLeft: 'auto' }}>
|
||||
<CardCheckbox
|
||||
isChecked={isChecked}
|
||||
isChecked={props.isChecked}
|
||||
handleChanged={handleCheckChanged}
|
||||
/>
|
||||
</SpanBox>
|
||||
|
||||
@ -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 ? (
|
||||
<SpanBox css={{ marginLeft: 'auto' }}>
|
||||
<CardCheckbox
|
||||
isChecked={isChecked}
|
||||
isChecked={props.isChecked}
|
||||
handleChanged={handleCheckChanged}
|
||||
/>
|
||||
</SpanBox>
|
||||
|
||||
@ -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"
|
||||
/>
|
||||
<DropdownSeparator />
|
||||
{props.startSelectMultiple && (
|
||||
<DropdownOption
|
||||
onSelect={() => headerDropdownActionHandler('begin-select-multiple')}
|
||||
onSelect={() =>
|
||||
props.startSelectMultiple && props.startSelectMultiple()
|
||||
}
|
||||
title="Select Multiple"
|
||||
/>
|
||||
)}
|
||||
{props.showAddLinkModal && (
|
||||
<DropdownOption
|
||||
onSelect={() => props.showAddLinkModal && props.showAddLinkModal()}
|
||||
title="Add Link"
|
||||
/>
|
||||
)}
|
||||
<DropdownSeparator />
|
||||
|
||||
<DropdownOption
|
||||
|
||||
@ -40,7 +40,7 @@ import { EditLibraryItemModal } from './EditItemModals'
|
||||
import { EmptyLibrary } from './EmptyLibrary'
|
||||
import { HighlightItemsLayout } from './HighlightsLayout'
|
||||
import { LibraryFilterMenu } from './LibraryFilterMenu'
|
||||
import { LibraryHeader } from './LibraryHeader'
|
||||
import { LibraryHeader, MultiSelectMode } from './LibraryHeader'
|
||||
import { UploadModal } from '../UploadModal'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
@ -116,6 +116,7 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
} else {
|
||||
setMode('reads')
|
||||
}
|
||||
setMultiSelectMode('off')
|
||||
}, [queryInputs])
|
||||
|
||||
useEffect(() => {
|
||||
@ -521,11 +522,58 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
)
|
||||
useFetchMore(handleFetchMore)
|
||||
|
||||
const [checkedItems, setCheckedItems] = useState<string[]>([])
|
||||
const [multiSelectMode, setMultiSelectMode] = useState<MultiSelectMode>('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 (
|
||||
<HomeFeedGrid
|
||||
items={libraryItems}
|
||||
actionHandler={handleCardAction}
|
||||
reloadItems={mutate}
|
||||
setIsChecked={setIsChecked}
|
||||
itemIsChecked={itemIsChecked}
|
||||
multiSelectMode={multiSelectMode}
|
||||
setMultiSelectMode={setMultiSelectMode}
|
||||
searchTerm={queryInputs.searchQuery}
|
||||
gridContainerRef={gridContainerRef}
|
||||
mode={mode}
|
||||
@ -572,6 +620,11 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
setLinkToEdit={setLinkToEdit}
|
||||
linkToUnsubscribe={linkToUnsubscribe}
|
||||
setLinkToUnsubscribe={setLinkToUnsubscribe}
|
||||
numItemsSelected={
|
||||
multiSelectMode == 'search'
|
||||
? itemsPages?.[0].search.pageInfo.totalCount || 0
|
||||
: checkedItems.length
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -609,6 +662,12 @@ type HomeFeedContentProps = {
|
||||
action: LinkedItemCardAction,
|
||||
item: LibraryItem | undefined
|
||||
) => Promise<void>
|
||||
|
||||
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 (
|
||||
<VStack
|
||||
@ -641,13 +701,14 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
||||
updateLayout={updateLayout}
|
||||
searchTerm={props.searchTerm}
|
||||
applySearchQuery={(searchQuery: string) => {
|
||||
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)}
|
||||
/>
|
||||
<HStack css={{ width: '100%', height: '100%' }}>
|
||||
<LibraryFilterMenu
|
||||
@ -673,7 +734,8 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
||||
<LibraryItemsLayout
|
||||
viewer={viewerData?.me}
|
||||
layout={layout}
|
||||
inMultiSelect={inMultiSelect}
|
||||
inMultiSelect={props.multiSelectMode !== 'off'}
|
||||
isChecked={props.itemIsChecked}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
@ -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') {
|
||||
|
||||
@ -67,10 +67,10 @@ export function LibraryFilterMenu(props: LibraryFilterMenuProps): JSX.Element {
|
||||
<SavedSearches {...props} />
|
||||
<Subscriptions {...props} />
|
||||
<Labels {...props} />
|
||||
|
||||
{/*
|
||||
<AddLinkButton
|
||||
showAddLinkModal={() => props.setShowAddLinkModal(true)}
|
||||
/>
|
||||
/> */}
|
||||
<Box css={{ height: '250px ' }} />
|
||||
</Box>
|
||||
{/* This spacer pushes library content to the right of
|
||||
|
||||
@ -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 {
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* <MagnifyingGlass size={20} color="#898989" /> */}
|
||||
{/* */}
|
||||
<ControlButtonBox
|
||||
layout={props.layout}
|
||||
updateLayout={props.updateLayout}
|
||||
inMultiSelect={props.inMultiSelect}
|
||||
setInMultiSelect={props.setInMultiSelect}
|
||||
numItemsSelected={props.numItemsSelected}
|
||||
multiSelectMode={props.multiSelectMode}
|
||||
setMultiSelectMode={props.setMultiSelectMode}
|
||||
showAddLinkModal={props.showAddLinkModal}
|
||||
/>
|
||||
</HStack>
|
||||
)
|
||||
@ -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(
|
||||
<Button
|
||||
style="cancel"
|
||||
onClick={(e) => {
|
||||
props.setInMultiSelect(false)
|
||||
props.setMultiSelectMode('off')
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
@ -468,14 +480,23 @@ function SearchControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
<PrimaryDropdown
|
||||
showThemeSection={true}
|
||||
startSelectMultiple={() => {
|
||||
props.setInMultiSelect(true)
|
||||
props.setMultiSelectMode('none')
|
||||
}}
|
||||
showAddLinkModal={props.showAddLinkModal}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
const [isChecked, setIsChecked] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (props.multiSelectMode === 'off' || props.multiSelectMode === 'none') {
|
||||
setIsChecked(false)
|
||||
}
|
||||
}, [props.multiSelectMode])
|
||||
|
||||
const breakpoints =
|
||||
props.layout == 'GRID_LAYOUT'
|
||||
? {
|
||||
@ -510,7 +531,7 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
<>
|
||||
<HStack
|
||||
alignment="center"
|
||||
distribution={props.inMultiSelect ? 'center' : 'start'}
|
||||
distribution={props.multiSelectMode !== 'off' ? 'center' : 'start'}
|
||||
css={{
|
||||
gap: '10px',
|
||||
'@mdDown': {
|
||||
@ -519,11 +540,24 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
...breakpoints,
|
||||
}}
|
||||
>
|
||||
{props.inMultiSelect && (
|
||||
<SpanBox css={{ flex: 1 }}>
|
||||
<Button css={{ p: '5px', gap: '5px' }}>
|
||||
<CardCheckbox isChecked={false} handleChanged={() => {}} />
|
||||
|
||||
{props.multiSelectMode !== 'off' && (
|
||||
<SpanBox
|
||||
css={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
gap: '2px',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<CardCheckbox
|
||||
isChecked={isChecked}
|
||||
handleChanged={() => {
|
||||
const newValue = !isChecked
|
||||
props.setMultiSelectMode(newValue ? 'visible' : 'none')
|
||||
setIsChecked(newValue)
|
||||
}}
|
||||
/>
|
||||
<SpanBox css={{ pt: '2px' }}>
|
||||
<Dropdown
|
||||
triggerElement={
|
||||
<CaretDown
|
||||
@ -533,18 +567,43 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DropdownOption onSelect={() => {}} title="All Visible" />
|
||||
<DropdownOption
|
||||
onSelect={() => {}}
|
||||
title="All Matching Search"
|
||||
onSelect={() => {
|
||||
setIsChecked(true)
|
||||
props.setMultiSelectMode('visible')
|
||||
}}
|
||||
title="All"
|
||||
/>
|
||||
<DropdownOption
|
||||
onSelect={() => {
|
||||
setIsChecked(true)
|
||||
props.setMultiSelectMode('search')
|
||||
}}
|
||||
title="All matching search"
|
||||
/>
|
||||
</Dropdown>
|
||||
</Button>
|
||||
</SpanBox>
|
||||
<SpanBox
|
||||
css={{
|
||||
paddingLeft: '10px',
|
||||
fontSize: '12px',
|
||||
fontWeight: '600',
|
||||
fontFamily: '$inter',
|
||||
}}
|
||||
>
|
||||
{props.numItemsSelected} selected
|
||||
</SpanBox>
|
||||
</SpanBox>
|
||||
)}
|
||||
{props.inMultiSelect ? (
|
||||
{props.multiSelectMode !== 'off' ? (
|
||||
<>
|
||||
<MultiSelectControlButtonBox {...props} />
|
||||
<MultiSelectControlButtonBox
|
||||
{...props}
|
||||
// setInMultiSelect={(set: boolean) => {
|
||||
// setIsChecked(false)
|
||||
// props.setInMultiSelect(set)
|
||||
// }}
|
||||
/>
|
||||
<SpanBox css={{ flex: 1 }}></SpanBox>
|
||||
</>
|
||||
) : (
|
||||
@ -586,7 +645,7 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
|
||||
layout={props.layout}
|
||||
updateLayout={props.updateLayout}
|
||||
startSelectMultiple={() => {
|
||||
props.setInMultiSelect(true)
|
||||
props.setMultiSelectMode('none')
|
||||
}}
|
||||
/>
|
||||
</HStack>
|
||||
|
||||
Reference in New Issue
Block a user