From e6eadf75f0e62457327dafe137f49727159b6012 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 7 Jun 2023 18:12:19 +0800 Subject: [PATCH 01/38] Some menu and bulk selection UI --- packages/web/components/elements/Button.tsx | 35 +++ packages/web/components/elements/Checkbox.tsx | 53 ++-- .../patterns/LibraryCards/CardTypes.tsx | 1 + .../patterns/LibraryCards/LibraryGridCard.tsx | 54 ++-- .../patterns/LibraryCards/LibraryListCard.tsx | 45 ++-- .../components/templates/PrimaryDropdown.tsx | 16 ++ .../templates/homeFeed/HomeFeedContainer.tsx | 12 +- .../templates/homeFeed/LibraryFilterMenu.tsx | 22 +- .../templates/homeFeed/LibraryHeader.tsx | 247 ++++++++++++++---- packages/web/pages/home.tsx | 2 +- 10 files changed, 378 insertions(+), 109 deletions(-) diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx index 313f5fff5..dd7e39954 100644 --- a/packages/web/components/elements/Button.tsx +++ b/packages/web/components/elements/Button.tsx @@ -106,6 +106,7 @@ export const Button = styled('button', { borderRadius: '$3', px: '$3', py: '$2', + gap: '5px', cursor: 'pointer', border: '1px solid $grayBgSubtle', bg: '$grayBgSubtle', @@ -113,6 +114,40 @@ export const Button = styled('button', { border: '1px solid $grayBorderHover', }, }, + outline: { + display: 'flex', + borderRadius: '5px', + alignItems: 'center', + px: '15px', + py: '5px', + gap: '10px', + fontSize: '12px', + fontWeight: '600', + fontFamily: '$inter', + cursor: 'pointer', + border: '1px solid $thBorderSubtle', + bg: 'transparent', + '&:hover': { + border: '1px solid $grayBorderHover', + }, + }, + cancel: { + display: 'flex', + borderRadius: '5px', + alignItems: 'center', + px: '15px', + py: '5px', + gap: '10px', + fontSize: '12px', + fontWeight: '600', + fontFamily: '$inter', + cursor: 'pointer', + bg: '$thBackground4', + border: '1px solid $thBackground4', + '&:hover': { + border: '1px solid $grayBorderHover', + }, + }, modalOption: { style: 'ghost', height: '52px', diff --git a/packages/web/components/elements/Checkbox.tsx b/packages/web/components/elements/Checkbox.tsx index f09997077..64d11ace6 100644 --- a/packages/web/components/elements/Checkbox.tsx +++ b/packages/web/components/elements/Checkbox.tsx @@ -2,21 +2,39 @@ import React from 'react' import { styled } from '@stitches/react' import * as CheckboxPrimitive from '@radix-ui/react-checkbox' import { CheckIcon } from './images/CheckIcon' +import { Box } from './LayoutPrimitives' +import { Check } from 'phosphor-react' -const Checkbox = styled(CheckboxPrimitive.Root, { +// const Checkbox = styled(CheckboxPrimitive.Root, { +// all: 'unset', +// width: 15, +// height: 15, +// borderRadius: 2, +// display: 'flex', +// alignItems: 'center', +// justifyContent: 'center', +// }) + +// const CheckboxIndicator = styled(CheckboxPrimitive.Indicator, { +// color: '#FFFFFF', +// }) + +const CheckboxRoot = styled(CheckboxPrimitive.Root, { all: 'unset', - width: 16, - height: 16, - minWidth: 16, - minHeight: 16, - borderRadius: 3, + backgroundColor: 'white', + width: 25, + height: 25, + borderRadius: 4, display: 'flex', alignItems: 'center', justifyContent: 'center', + boxShadow: `0 2px 10px black`, + '&:hover': { backgroundColor: 'violet' }, + '&:focus': { boxShadow: `0 0 0 2px black` }, }) const CheckboxIndicator = styled(CheckboxPrimitive.Indicator, { - color: '#FFFFFF', + color: 'violet', }) export const CheckboxComponent: React.FC<{ @@ -26,18 +44,15 @@ export const CheckboxComponent: React.FC<{ const toggleChecked = () => setChecked(!checked) return ( - - - - - +
+ + + + + + + +
) } diff --git a/packages/web/components/patterns/LibraryCards/CardTypes.tsx b/packages/web/components/patterns/LibraryCards/CardTypes.tsx index cf19502e1..070eed429 100644 --- a/packages/web/components/patterns/LibraryCards/CardTypes.tsx +++ b/packages/web/components/patterns/LibraryCards/CardTypes.tsx @@ -19,5 +19,6 @@ export type LinkedItemCardProps = { item: LibraryItemNode layout: LayoutType viewer: UserBasicData + inMultiSelect: boolean handleAction: (action: LinkedItemCardAction) => void } diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 16c1bbef3..2429481c6 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -19,6 +19,7 @@ import { TitleStyle, } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' +import CheckboxComponent from '../../elements/Checkbox' dayjs.extend(relativeTime) @@ -104,25 +105,40 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { distribution="start" > - - setMenuOpen(open)} - actionHandler={props.handleAction} - triggerElement={ - - } - /> - + {props.inMultiSelect ? ( + + { + event.preventDefault() + }} + > + + ) : ( + + setMenuOpen(open)} + actionHandler={props.handleAction} + triggerElement={ + + } + /> + + )} - - setMenuOpen(open)} - actionHandler={props.handleAction} - triggerElement={ - - } - /> - + {props.inMultiSelect ? ( + + + + ) : ( + + setMenuOpen(open)} + actionHandler={props.handleAction} + triggerElement={ + + } + /> + + )} void + + startSelectMultiple?: () => void } export type HeaderDropdownAction = @@ -35,6 +37,7 @@ export type HeaderDropdownAction = | 'navigate-to-integrations' | 'increaseFontSize' | 'decreaseFontSize' + | 'begin-select-multiple' | 'logout' export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element { @@ -62,6 +65,11 @@ 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 @@ -153,6 +161,14 @@ export function PrimaryDropdown(props: PrimaryDropdownProps): JSX.Element { onSelect={() => headerDropdownActionHandler('navigate-to-labels')} title="Labels" /> + {props.startSelectMultiple && ( + headerDropdownActionHandler('begin-select-multiple')} + title="Select Multiple" + /> + )} + + headerDropdownActionHandler('navigate-to-api')} title="API Keys" diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index e2be6936b..c5e842d89 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -1,4 +1,3 @@ -import axios from 'axios' import { Action, createAction, useKBar, useRegisterActions } from 'kbar' import debounce from 'lodash/debounce' import { useRouter } from 'next/router' @@ -628,6 +627,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { ) const [showFilterMenu, setShowFilterMenu] = useState(false) + const [inMultiSelect, setInMultiSelect] = useState(false) return ( )} @@ -686,6 +689,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { type LibraryItemsLayoutProps = { layout: LayoutType viewer?: UserBasicData + inMultiSelect: boolean } & HomeFeedContentProps function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element { @@ -753,6 +757,7 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element { setLinkToUnsubscribe={props.setLinkToUnsubscribe} setShowRemoveLinkConfirmation={setShowRemoveLinkConfirmation} actionHandler={props.actionHandler} + inMultiSelect={props.inMultiSelect} /> )} void setShowRemoveLinkConfirmation: (show: true) => void + inMultiSelect: boolean + actionHandler: ( action: LinkedItemCardAction, item: LibraryItem | undefined @@ -932,7 +939,7 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element { outline: 'none', }, '&> div': { - bg: '$thBackground3', + bg: '$thBackground', }, '&:focus': { outline: 'none', @@ -956,6 +963,7 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element { layout={props.layout} item={linkedItem.node} viewer={props.viewer} + inMultiSelect={props.inMultiSelect} handleAction={(action: LinkedItemCardAction) => { if (action === 'delete') { props.setShowRemoveLinkConfirmation(true) diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx index 93e08471e..a682b87b6 100644 --- a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx +++ b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx @@ -11,6 +11,7 @@ import { theme } from '../../tokens/stitches.config' import { currentThemeName } from '../../../lib/themeUpdater' import { useRegisterActions } from 'kbar' import { HEADER_HEIGHT } from './HeaderSpacer' +import { LogoBox } from '../../elements/LogoBox' export const LIBRARY_LEFT_MENU_WIDTH = '233px' @@ -30,12 +31,12 @@ export function LibraryFilterMenu(props: LibraryFilterMenuProps): JSX.Element { + + + + diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 0bb757279..b9ad73e8a 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -1,18 +1,30 @@ -import { useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { Box, HStack, VStack } from '../../elements/LayoutPrimitives' import { theme } from '../../tokens/stitches.config' import { FormInput } from '../../elements/FormElements' import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts' import { Button, IconButton } from '../../elements/Button' -import { FunnelSimple, MagnifyingGlass, X } from 'phosphor-react' -import { ListSelectorIcon } from '../../elements/images/ListSelectorIcon' -import { GridSelectorIcon } from '../../elements/images/GridSelectorIcon' +import { + ArchiveBox, + FunnelSimple, + ListBullets, + MagnifyingGlass, + Prohibit, + SquaresFour, + TagSimple, + TrashSimple, + X, +} from 'phosphor-react' import { LayoutType } from './HomeFeedContainer' import { PrimaryDropdown } from '../PrimaryDropdown' -import { LogoBox } from '../../elements/LogoBox' import { OmnivoreSmallLogo } from '../../elements/images/OmnivoreNameLogo' import { HeaderSpacer, HEADER_HEIGHT } from './HeaderSpacer' +import { LIBRARY_LEFT_MENU_WIDTH } from '../../templates/homeFeed/LibraryFilterMenu' +import { + ScrollOffsetChangeset, + useScrollWatcher, +} from '../../../lib/hooks/useScrollWatcher' type LibraryHeaderProps = { layout: LayoutType @@ -23,9 +35,24 @@ type LibraryHeaderProps = { showFilterMenu: boolean setShowFilterMenu: (show: boolean) => void + + inMultiSelect: boolean + setInMultiSelect: (set: boolean) => void } export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { + const [isScrolled, setIsScrolled] = useState(props.inMultiSelect) + + useEffect(() => { + if (window.scrollY > 5 || props.inMultiSelect) { + setIsScrolled(true) + } + }) + + useScrollWatcher((changeset: ScrollOffsetChangeset) => { + setIsScrolled(window.scrollY > 5) + }, 0) + return ( <> {/* These will display/hide depending on breakpoints */} @@ -59,7 +88,7 @@ function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element { return ( - - + {/* */} + {/* */} ) @@ -114,6 +145,8 @@ function SmallHeaderLayout(props: LibraryHeaderProps): JSX.Element { layout={props.layout} updateLayout={props.updateLayout} setShowInlineSearch={setShowInlineSearch} + inMultiSelect={props.inMultiSelect} + setInMultiSelect={props.setInMultiSelect} /> )} @@ -317,52 +350,171 @@ type ControlButtonBoxProps = { layout: LayoutType updateLayout: (layout: LayoutType) => void setShowInlineSearch?: (show: boolean) => void + + inMultiSelect: boolean + setInMultiSelect: (set: boolean) => void +} + +function MultiSelectControlButtonBox( + props: ControlButtonBoxProps +): JSX.Element { + return ( + + + + + + + ) +} + +function SearchControlButtonBox(props: ControlButtonBoxProps): JSX.Element { + return ( + <> + {}} /> + + { + props.setInMultiSelect(true) + }} + /> + + ) } function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element { + const breakpoints = + props.layout == 'GRID_LAYOUT' + ? { + minWidth: '121px', + + '@xlgDown': { + width: '320px', + }, + '@smDown': { + width: '320px', + }, + '@media (min-width: 930px)': { + width: '640px', + }, + '@media (min-width: 1280px)': { + width: '1000px', + }, + '@media (min-width: 1600px)': { + width: '1340px', + }, + } + : { + width: '900px', + '@xlgDown': { + width: '90%', + }, + '@xxl': { + width: '1200px', + }, + } return ( <> - - - - + {props.inMultiSelect ? ( + + ) : ( + + )} {props.setShowInlineSearch && ( @@ -398,6 +550,9 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element { showThemeSection={true} layout={props.layout} updateLayout={props.updateLayout} + startSelectMultiple={() => { + props.setInMultiSelect(true) + }} /> )} diff --git a/packages/web/pages/home.tsx b/packages/web/pages/home.tsx index 714abbeaa..99d52d835 100644 --- a/packages/web/pages/home.tsx +++ b/packages/web/pages/home.tsx @@ -18,7 +18,7 @@ function LoadedContent(): JSX.Element { From d4aa715b5b54fd77f75abba4407725517029cd14 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 7 Jun 2023 21:16:48 +0800 Subject: [PATCH 02/38] Add checkboxes on library cards in multi select mode --- packages/web/components/elements/Checkbox.tsx | 53 ++-- .../patterns/LibraryCards/CardTypes.tsx | 2 + .../LibraryCards/LibraryCardStyles.tsx | 15 + .../patterns/LibraryCards/LibraryGridCard.tsx | 292 +++++++++--------- .../patterns/LibraryCards/LibraryListCard.tsx | 178 ++++++----- 5 files changed, 283 insertions(+), 257 deletions(-) diff --git a/packages/web/components/elements/Checkbox.tsx b/packages/web/components/elements/Checkbox.tsx index 64d11ace6..f09997077 100644 --- a/packages/web/components/elements/Checkbox.tsx +++ b/packages/web/components/elements/Checkbox.tsx @@ -2,39 +2,21 @@ import React from 'react' import { styled } from '@stitches/react' import * as CheckboxPrimitive from '@radix-ui/react-checkbox' import { CheckIcon } from './images/CheckIcon' -import { Box } from './LayoutPrimitives' -import { Check } from 'phosphor-react' -// const Checkbox = styled(CheckboxPrimitive.Root, { -// all: 'unset', -// width: 15, -// height: 15, -// borderRadius: 2, -// display: 'flex', -// alignItems: 'center', -// justifyContent: 'center', -// }) - -// const CheckboxIndicator = styled(CheckboxPrimitive.Indicator, { -// color: '#FFFFFF', -// }) - -const CheckboxRoot = styled(CheckboxPrimitive.Root, { +const Checkbox = styled(CheckboxPrimitive.Root, { all: 'unset', - backgroundColor: 'white', - width: 25, - height: 25, - borderRadius: 4, + width: 16, + height: 16, + minWidth: 16, + minHeight: 16, + borderRadius: 3, display: 'flex', alignItems: 'center', justifyContent: 'center', - boxShadow: `0 2px 10px black`, - '&:hover': { backgroundColor: 'violet' }, - '&:focus': { boxShadow: `0 0 0 2px black` }, }) const CheckboxIndicator = styled(CheckboxPrimitive.Indicator, { - color: 'violet', + color: '#FFFFFF', }) export const CheckboxComponent: React.FC<{ @@ -44,15 +26,18 @@ export const CheckboxComponent: React.FC<{ const toggleChecked = () => setChecked(!checked) return ( -
- - - - - - - -
+ + + + + ) } diff --git a/packages/web/components/patterns/LibraryCards/CardTypes.tsx b/packages/web/components/patterns/LibraryCards/CardTypes.tsx index 070eed429..278748d10 100644 --- a/packages/web/components/patterns/LibraryCards/CardTypes.tsx +++ b/packages/web/components/patterns/LibraryCards/CardTypes.tsx @@ -21,4 +21,6 @@ export type LinkedItemCardProps = { viewer: UserBasicData inMultiSelect: boolean handleAction: (action: LinkedItemCardAction) => void + + isHovered?: boolean } diff --git a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx index b224994d7..1775c6143 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryCardStyles.tsx @@ -146,3 +146,18 @@ export function LibraryItemMetadata( ) } + +type CardCheckBoxProps = { + isChecked: boolean + handleChanged: () => void +} + +export function CardCheckbox(props: CardCheckBoxProps): JSX.Element { + return ( + + ) +} diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 2429481c6..6484079ef 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -4,22 +4,21 @@ import type { LinkedItemCardProps } from './CardTypes' import { CoverImage } from '../../elements/CoverImage' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' -import { useMemo, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { DotsThreeVertical } from 'phosphor-react' import Link from 'next/link' import { CardMenu } from '../CardMenu' import { AuthorInfoStyle, + CardCheckbox, DescriptionStyle, LibraryItemMetadata, MenuStyle, MetaStyle, siteName, - timeAgo, TitleStyle, } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' -import CheckboxComponent from '../../elements/Checkbox' dayjs.extend(relativeTime) @@ -53,12 +52,8 @@ export function ProgressBar(props: ProgressBarProps): JSX.Element { ) } -// Component export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { const [isHovered, setIsHovered] = useState(false) - const [menuOpen, setMenuOpen] = useState(false) - - const originText = siteName(props.item.originalArticleUrl, props.item.url) return ( - - + ) : ( + - - - {props.inMultiSelect ? ( - - { - event.preventDefault() - }} - > - - ) : ( - - setMenuOpen(open)} - actionHandler={props.handleAction} - triggerElement={ - - } - /> - - )} - - - - {props.item.title} - - {props.item.description} - - {props.item.author} - {props.item.author && originText && ' | '} - - {originText} - - - - - - - - - {sortedLabels(props.item.labels).map( - ({ name, color }, index) => ( - - ) - )} - - - {props.item.image && ( - { - ;(e.target as HTMLElement).style.display = 'none' - }} - /> - )} - - - - - + + + + )} ) } + +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]) + + return ( + <> + + + {props.inMultiSelect ? ( + + + + ) : ( + + setMenuOpen(open)} + actionHandler={props.handleAction} + triggerElement={ + + } + /> + + )} + + + + {props.item.title} + + {props.item.description} + + {props.item.author} + {props.item.author && originText && ' | '} + {originText} + + + + + + + + {sortedLabels(props.item.labels).map(({ name, color }, index) => ( + + ))} + + + {props.item.image && ( + { + ;(e.target as HTMLElement).style.display = 'none' + }} + /> + )} + + + + + ) +} diff --git a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx index c2ead3bd9..ff57c9c2f 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryListCard.tsx @@ -1,27 +1,23 @@ import { Box, VStack, HStack, SpanBox } from '../../elements/LayoutPrimitives' import { LabelChip } from '../../elements/LabelChip' import type { LinkedItemCardProps } from './CardTypes' -import { useMemo, useState } from 'react' +import { useCallback, useState } from 'react' import { DotsThree } from 'phosphor-react' import Link from 'next/link' import { CardMenu } from '../CardMenu' import { AuthorInfoStyle, + CardCheckbox, LibraryItemMetadata, MenuStyle, MetaStyle, siteName, - timeAgo, TitleStyle, } from './LibraryCardStyles' import { sortedLabels } from '../../../lib/labelsSort' -import { Checkbox } from '@radix-ui/react-checkbox' export function LibraryListCard(props: LinkedItemCardProps): JSX.Element { const [isHovered, setIsHovered] = useState(false) - const [menuOpen, setMenuOpen] = useState(false) - - const originText = siteName(props.item.originalArticleUrl, props.item.url) return ( - - + ) : ( + - - - {props.inMultiSelect ? ( - - - - ) : ( - - setMenuOpen(open)} - actionHandler={props.handleAction} - triggerElement={ - - } - /> - - )} - - - {props.item.title} - - {props.item.author} - {props.item.author && originText && ' | '} - - {originText} - - - - - - {sortedLabels(props.item.labels).map( - ({ name, color }, index) => ( - - ) - )} - - - - - + + + + )} ) } + +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]) + + return ( + <> + + + {props.inMultiSelect ? ( + + + + ) : ( + + setMenuOpen(open)} + actionHandler={props.handleAction} + triggerElement={ + + } + /> + + )} + + + {props.item.title} + + {props.item.author} + {props.item.author && originText && ' | '} + {originText} + + + + + {sortedLabels(props.item.labels).map(({ name, color }, index) => ( + + ))} + + + + + ) +} From 83c8aff44d3650c5afebbb437ea4c69fb7d41215 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Wed, 7 Jun 2023 21:41:53 +0800 Subject: [PATCH 03/38] Handle selection in the header --- .../templates/homeFeed/LibraryHeader.tsx | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index b9ad73e8a..46f859400 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -1,5 +1,5 @@ -import { useEffect, useRef, useState } from 'react' -import { Box, HStack, VStack } from '../../elements/LayoutPrimitives' +import { useCallback, useEffect, useRef, useState } from 'react' +import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' import { theme } from '../../tokens/stitches.config' import { FormInput } from '../../elements/FormElements' import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts' @@ -7,6 +7,7 @@ import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboard import { Button, IconButton } from '../../elements/Button' import { ArchiveBox, + CaretDown, FunnelSimple, ListBullets, MagnifyingGlass, @@ -25,6 +26,8 @@ import { ScrollOffsetChangeset, useScrollWatcher, } from '../../../lib/hooks/useScrollWatcher' +import { CardCheckbox } from '../../patterns/LibraryCards/LibraryCardStyles' +import { Dropdown, DropdownOption } from '../../elements/DropdownElements' type LibraryHeaderProps = { layout: LayoutType @@ -358,6 +361,12 @@ type ControlButtonBoxProps = { function MultiSelectControlButtonBox( props: ControlButtonBoxProps ): JSX.Element { + const [isChecked, setIsChecked] = useState(false) + + const handleCheckChanged = useCallback(() => { + setIsChecked(!isChecked) + }, [isChecked]) + return ( + + )} {props.inMultiSelect ? ( - + <> + + + ) : ( )} From e0cdbdb0e7e7961c824cffdf1850cc657a82abd0 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 8 Jun 2023 12:47:04 +0800 Subject: [PATCH 04/38] 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') }} /> From b45b040ceadbfea0949ed3f39ee45dd7ad809ef3 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 8 Jun 2023 13:56:11 +0800 Subject: [PATCH 05/38] Keyboard controls for multi select --- .../templates/homeFeed/HomeFeedContainer.tsx | 44 +++++++++++++++---- .../templates/homeFeed/LibraryHeader.tsx | 12 ++--- .../keyboardShortcuts/navigationShortcuts.ts | 16 +++++++ 3 files changed, 56 insertions(+), 16 deletions(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 524795b92..a709bbc8c 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -331,6 +331,23 @@ export function HomeFeedContainer(): JSX.Element { return labelsTarget || linkToEdit || linkToRemove || linkToUnsubscribe }, [labelsTarget, linkToEdit, linkToRemove, linkToUnsubscribe]) + const [checkedItems, setCheckedItems] = useState([]) + const [multiSelectMode, setMultiSelectMode] = useState('off') + + const selectActiveArticle = useCallback(() => { + console.log('selecting article: ', activeItem) + if (activeItem) { + if (multiSelectMode === 'off') { + console.log('setting ') + setMultiSelectMode('none') + } + const itemId = activeItem.node.id + const isChecked = itemIsChecked(itemId) + console.log('setting is checked: ', isChecked, itemId) + setIsChecked(itemId, !isChecked) + } + }, [activeItem, multiSelectMode, checkedItems]) + useKeyboardShortcuts( libraryListCommands((action) => { const columnCount = (container: HTMLDivElement) => { @@ -348,7 +365,16 @@ export function HomeFeedContainer(): JSX.Element { switch (action) { case 'openArticle': - handleCardAction('showDetail', activeItem) + if (multiSelectMode !== 'off' && activeItem) { + const itemId = activeItem.node.id + const isChecked = itemIsChecked(itemId) + setIsChecked(itemId, !isChecked) + } else { + handleCardAction('showDetail', activeItem) + } + break + case 'selectArticle': + selectActiveArticle() break case 'openOriginalArticle': handleCardAction('showOriginal', activeItem) @@ -435,6 +461,11 @@ export function HomeFeedContainer(): JSX.Element { case 'sortAscending': setQueryInputs({ ...queryInputs, sortDescending: false }) break + case 'beginMultiSelect': + if (multiSelectMode == 'off') { + setMultiSelectMode('none') + } + break } }) ) @@ -522,23 +553,22 @@ export function HomeFeedContainer(): JSX.Element { ) useFetchMore(handleFetchMore) - const [checkedItems, setCheckedItems] = useState([]) - const [multiSelectMode, setMultiSelectMode] = useState('off') - const setIsChecked = useCallback( (itemId: string, set: boolean) => { + console.log('setting is checked with list: ', checkedItems) 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.splice(checkedItems.indexOf(itemId)) + setCheckedItems([...checkedItems]) } }, [checkedItems] ) useEffect(() => { - console.log('switching on multiselect mode: ', multiSelectMode) + console.log(' -- multiselect mode: ', multiSelectMode) switch (multiSelectMode) { case 'off': case 'none': @@ -687,8 +717,6 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { const [showFilterMenu, setShowFilterMenu] = useState(false) - console.log('props.multiSelectMode: ', props.multiSelectMode) - return ( { - if (window.scrollY > 5 || props.multiSelectMode != 'off') { - setShowBackground(true) - } - }) + setShowBackground(window.scrollY > 5 || props.multiSelectMode !== 'off') + }, [props.multiSelectMode]) useScrollWatcher((changeset: ScrollOffsetChangeset) => { - setShowBackground(window.scrollY > 5) + setShowBackground(window.scrollY > 5 || props.multiSelectMode !== 'off') }, 0) return ( diff --git a/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts b/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts index 2c4bd0bab..f01044200 100644 --- a/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts +++ b/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts @@ -74,6 +74,7 @@ export function primaryCommands( type LibraryListKeyboardAction = | 'openArticle' + | 'selectArticle' | 'openOriginalArticle' | 'moveFocusToNextListItem' | 'moveFocusToPreviousListItem' @@ -88,6 +89,7 @@ type LibraryListKeyboardAction = | 'shareItem' | 'showAddLinkModal' | 'showEditLabelsModal' + | 'beginMultiSelect' export function libraryListCommands( actionHandler: (action: LibraryListKeyboardAction) => void @@ -99,6 +101,14 @@ export function libraryListCommands( shortcutKeyDescription: 'enter/return', callback: () => actionHandler('openArticle'), }, + { + shortcutKeys: ['x'], + actionDescription: 'Select article', + shortcutKeyDescription: 'x', + callback: () => { + actionHandler('selectArticle') + }, + }, { shortcutKeys: ['o'], actionDescription: 'Open original article', @@ -123,6 +133,12 @@ export function libraryListCommands( shortcutKeyDescription: 'k or left arrow', callback: () => actionHandler('moveFocusToPreviousListItem'), }, + { + shortcutKeys: ['m', 's'], + actionDescription: 'Begin multi select', + shortcutKeyDescription: 'm then s', + callback: () => actionHandler('beginMultiSelect'), + }, // { // shortcutKeys: ['e'], // actionDescription: 'Archive item', From 02b68a036a64885679c971e572510812c9dd2393 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 8 Jun 2023 14:13:43 +0800 Subject: [PATCH 06/38] Add in calls to bulk action --- .../templates/homeFeed/HomeFeedContainer.tsx | 42 ++++++++++++++++--- .../templates/homeFeed/LibraryHeader.tsx | 27 ++++++------ .../keyboardShortcuts/navigationShortcuts.ts | 7 ++++ 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index a709bbc8c..384259e03 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -42,6 +42,8 @@ import { HighlightItemsLayout } from './HighlightsLayout' import { LibraryFilterMenu } from './LibraryFilterMenu' import { LibraryHeader, MultiSelectMode } from './LibraryHeader' import { UploadModal } from '../UploadModal' +import { BulkAction } from '../../../lib/networking/mutations/bulkActionMutation' +import { bulkActionMutation } from '../../../lib/networking/mutations/bulkActionMutation' export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT' export type LibraryMode = 'reads' | 'highlights' @@ -302,13 +304,21 @@ export function HomeFeedContainer(): JSX.Element { } break case 'archive': - performActionOnItem('archive', item) + if (multiSelectMode !== 'off') { + performMultiSelectAction(BulkAction.ARCHIVE) + } else { + performActionOnItem('archive', item) + } break case 'unarchive': performActionOnItem('unarchive', item) break case 'delete': - performActionOnItem('delete', item) + if (multiSelectMode !== 'off') { + performMultiSelectAction(BulkAction.DELETE) + } else { + performActionOnItem('delete', item) + } break case 'mark-read': performActionOnItem('mark-read', item) @@ -466,6 +476,9 @@ export function HomeFeedContainer(): JSX.Element { setMultiSelectMode('none') } break + case 'endMultiSelect': + setMultiSelectMode('off') + break } }) ) @@ -555,12 +568,11 @@ export function HomeFeedContainer(): JSX.Element { const setIsChecked = useCallback( (itemId: string, set: boolean) => { - console.log('setting is checked with list: ', checkedItems) if (set && checkedItems.indexOf(itemId) === -1) { checkedItems.push(itemId) setCheckedItems([...checkedItems]) } else if (!set && checkedItems.indexOf(itemId) !== -1) { - checkedItems.splice(checkedItems.indexOf(itemId)) + checkedItems.splice(checkedItems.indexOf(itemId), 1) setCheckedItems([...checkedItems]) } }, @@ -568,7 +580,6 @@ export function HomeFeedContainer(): JSX.Element { ) useEffect(() => { - console.log(' -- multiselect mode: ', multiSelectMode) switch (multiSelectMode) { case 'off': case 'none': @@ -595,6 +606,23 @@ export function HomeFeedContainer(): JSX.Element { [checkedItems] ) + const performMultiSelectAction = useCallback( + (action: BulkAction) => { + console.log( + 'performing bulk action: ', + action, + 'mode', + mode, + checkedItems + ) + try { + // const query = multiSelectMode === 'some' ? + // const res = await bulkActionMutation(action, query) + } catch (err) {} + }, + [checkedItems] + ) + return ( boolean setMultiSelectMode: (mode: MultiSelectMode) => void numItemsSelected: number + + performMultiSelectAction: (action: BulkAction) => void } function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { @@ -737,6 +768,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { setMultiSelectMode={props.setMultiSelectMode} numItemsSelected={props.numItemsSelected} showAddLinkModal={() => props.setShowAddLinkModal(true)} + performMultiSelectAction={props.performMultiSelectAction} /> void + + performMultiSelectAction: (action: BulkAction) => void } export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { @@ -110,6 +113,7 @@ function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element { multiSelectMode={props.multiSelectMode} setMultiSelectMode={props.setMultiSelectMode} showAddLinkModal={props.showAddLinkModal} + performMultiSelectAction={props.performMultiSelectAction} /> ) @@ -155,6 +159,7 @@ function SmallHeaderLayout(props: LibraryHeaderProps): JSX.Element { multiSelectMode={props.multiSelectMode} setMultiSelectMode={props.setMultiSelectMode} showAddLinkModal={props.showAddLinkModal} + performMultiSelectAction={props.performMultiSelectAction} /> )} @@ -364,6 +369,8 @@ type ControlButtonBoxProps = { numItemsSelected: number multiSelectMode: MultiSelectMode setMultiSelectMode: (mode: MultiSelectMode) => void + + performMultiSelectAction: (action: BulkAction) => void } function MultiSelectControlButtonBox( @@ -379,12 +386,10 @@ function MultiSelectControlButtonBox( - - - )} - - {runningState == 'confirming' && ( - setRunningState('none')} - /> - )} - {runningState == 'completed' && ( - - - - )} - - {errorMessage && ( - {errorMessage} - )} -
-
- - ) -} From 36bb40a6f37c47a73f1e76821af59ca25f4c7118 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 8 Jun 2023 19:59:02 +0800 Subject: [PATCH 21/38] Add expectedCount --- packages/web/lib/networking/mutations/bulkActionMutation.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/web/lib/networking/mutations/bulkActionMutation.ts b/packages/web/lib/networking/mutations/bulkActionMutation.ts index 07ea621e2..b2f9cc133 100644 --- a/packages/web/lib/networking/mutations/bulkActionMutation.ts +++ b/packages/web/lib/networking/mutations/bulkActionMutation.ts @@ -26,7 +26,11 @@ export async function bulkActionMutation( $query: String! $expectedCount: Int ) { - bulkAction(query: $query, action: $action) { + bulkAction( + query: $query + action: $action + expectedCount: $expectedCount + ) { ... on BulkActionSuccess { success } From 4e2c69d6811746813338f9a7c9acd693911b41c0 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 9 Jun 2023 14:23:10 +0800 Subject: [PATCH 22/38] Some breakpoint improvements --- .../patterns/LibraryCards/LibraryGridCard.tsx | 2 +- .../templates/homeFeed/LibraryHeader.tsx | 51 ++++++------------- .../mutations/bulkActionMutation.ts | 1 + 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx index 9775ebbe4..66bf0b96b 100644 --- a/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx +++ b/packages/web/components/patterns/LibraryCards/LibraryGridCard.tsx @@ -69,7 +69,7 @@ export function LibraryGridCard(props: LinkedItemCardProps): JSX.Element { borderStyle: 'solid', borderColor: '$thBorderColor', cursor: 'pointer', - '@smDown': { + '@media (max-width: 930px)': { m: '15px', width: 'calc(100% - 30px)', }, diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 9ca749f0a..653a627a7 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -400,12 +400,10 @@ function MultiSelectControlButtonBox( - + */} + {showConfirmDelete && ( + { + props.performMultiSelectAction(BulkAction.DELETE) + }} + onOpenChange={(open: boolean) => { + setShowConfirmDelete(false) + }} + /> + )} ) } From 326fd06f9be4445f8f76139c90a858c6334ddda4 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 9 Jun 2023 18:32:21 +0800 Subject: [PATCH 29/38] Some library margin/padding improvements --- .../templates/homeFeed/HomeFeedContainer.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 395f3225d..4f672214d 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -1061,12 +1061,12 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element { display: 'grid', width: '100%', gridAutoRows: 'auto', - borderRadius: '8px', + borderRadius: '5px', gridGap: props.layout == 'LIST_LAYOUT' ? '0' : '20px', - marginTop: props.layout == 'LIST_LAYOUT' ? '21px' : '0', + marginTop: '0px', marginBottom: '0px', - paddingTop: props.layout == 'LIST_LAYOUT' ? '0' : '21px', - paddingBottom: props.layout == 'LIST_LAYOUT' ? '0px' : '21px', + paddingTop: '0', + paddingBottom: '0px', overflow: 'hidden', '@xlgDown': { border: 'unset', @@ -1105,7 +1105,7 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element { outline: 'none', }, '&> div': { - bg: '$thBackground', + bg: '$thBackground3', }, '&:focus': { outline: 'none', From 14c94eb52cfc52035b060208f28d117b843c14e4 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Fri, 9 Jun 2023 18:46:37 +0800 Subject: [PATCH 30/38] Display multi select tools on small screens --- .../templates/homeFeed/LibraryHeader.tsx | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 3edf6b49f..28a3e98a4 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -153,7 +153,7 @@ function SmallHeaderLayout(props: LibraryHeaderProps): JSX.Element { ) : ( <> - + {props.multiSelectMode === 'off' && } { + setSearchTerm(props.searchTerm ?? '') + }, [props.searchTerm]) + const border = props.compact ? focused ? '1px solid $omnivoreCtaYellow' @@ -514,18 +518,6 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element { } }, [props.multiSelectMode]) - const breakpoints = { - width: '95%', - '@media (min-width: 930px)': { - width: '640px', - }, - '@media (min-width: 1280px)': { - width: '1000px', - }, - '@media (min-width: 1600px)': { - width: '1340px', - }, - } return ( <> {props.multiSelectMode !== 'off' && ( @@ -546,6 +547,9 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element { display: 'flex', gap: '2px', alignItems: 'center', + '@mdDown': { + mx: '20px', + }, }} > - { - // setIsChecked(false) - // props.setInMultiSelect(set) - // }} - /> + ) : ( @@ -618,7 +616,7 @@ function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element { )} - {props.setShowInlineSearch && ( + {props.setShowInlineSearch && props.multiSelectMode === 'off' && ( Date: Fri, 9 Jun 2023 18:47:24 +0800 Subject: [PATCH 31/38] Top margin on library --- .../web/components/templates/homeFeed/HomeFeedContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 4f672214d..b60e14ec2 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -1063,7 +1063,7 @@ function LibraryItems(props: LibraryItemsProps): JSX.Element { gridAutoRows: 'auto', borderRadius: '5px', gridGap: props.layout == 'LIST_LAYOUT' ? '0' : '20px', - marginTop: '0px', + marginTop: '10px', marginBottom: '0px', paddingTop: '0', paddingBottom: '0px', From 2c9b9cc93269dcd43dc045d43bb9cb725a1dfca7 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 12 Jun 2023 12:35:51 +0800 Subject: [PATCH 32/38] Update the light mode library colours --- packages/web/components/tokens/stitches.config.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts index 73c1080bf..e544e190b 100644 --- a/packages/web/components/tokens/stitches.config.ts +++ b/packages/web/components/tokens/stitches.config.ts @@ -163,7 +163,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } = //utility overlay: 'rgba(63, 62, 60, 0.2)', - // New theme, special naming to keep things straigh + // New theme, special naming to keep things straight // once all switch over, we will rename thBackground: '#FFFFFF', thBackground2: '#F3F3F3', @@ -172,8 +172,8 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } = thBackground5: '#F5F5F5', thBackgroundActive: '#F9F9F9', thBackgroundContrast: '#FFFFFF', - thLeftMenuBackground: '#F2F3F6', - thLibraryBackground: '#FFFFFF', + thLeftMenuBackground: '#FCFCFC', + thLibraryBackground: '#F3F3F3', thTextContrast: '#1E1E1E', thTextContrast2: '#3D3D3D', From eacacbebb32698de650f8c0b09c58493895bb3d8 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 12 Jun 2023 13:41:03 +0800 Subject: [PATCH 33/38] Search bar and library menu colours --- .../templates/homeFeed/LibraryFilterMenu.tsx | 17 ++++++++++++----- .../templates/homeFeed/LibraryHeader.tsx | 2 +- .../web/components/tokens/stitches.config.ts | 10 ++++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx index fd8895a4b..f836895c4 100644 --- a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx +++ b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx @@ -243,7 +243,7 @@ function MenuPanel(props: MenuPanelProps): JSX.Element { fontWeight: '600', fontSize: '16px', lineHeight: '125%', - color: '$thTextContrast', + color: '$thLibraryMenuPrimary', pl: '10px', my: '20px', }} @@ -331,11 +331,14 @@ function FilterButton(props: FilterButtonProps): JSX.Element { width: '100%', maxWidth: '100%', height: '32px', - backgroundColor: selected ? '#FFEA9F' : 'unset', + + backgroundColor: selected ? '$thLibrarySelectionColor' : 'unset', fontSize: '14px', fontWeight: 'regular', fontFamily: '$display', - color: selected ? '#3D3D3D' : '$thTextSubtle', + color: selected + ? '$thLibraryMenuSecondary' + : '$thLibraryMenuUnselected', verticalAlign: 'middle', borderRadius: '3px', cursor: 'pointer', @@ -344,10 +347,14 @@ function FilterButton(props: FilterButtonProps): JSX.Element { whiteSpace: 'nowrap', alignItems: 'center', '&:hover': { - backgroundColor: selected ? '#FFEA9F' : '$thBackground4', + backgroundColor: selected + ? '$thLibrarySelectionColor' + : '$thBackground4', }, '&:active': { - backgroundColor: selected ? '#FFEA9F' : '$thBackground4', + backgroundColor: selected + ? '$thLibrarySelectionColor' + : '$thBackground4', }, }} onClick={(e) => { diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 28a3e98a4..24fe0999b 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -258,7 +258,7 @@ export function SearchBox(props: SearchBoxProps): JSX.Element { height: '38px', width: '100%', maxWidth: '521px', - bg: props.compact ? 'white' : '$thBackground2', + bg: '$thLibrarySearchbox', borderRadius: '6px', border: border, }} diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts index e544e190b..d37381370 100644 --- a/packages/web/components/tokens/stitches.config.ts +++ b/packages/web/components/tokens/stitches.config.ts @@ -174,6 +174,11 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } = thBackgroundContrast: '#FFFFFF', thLeftMenuBackground: '#FCFCFC', thLibraryBackground: '#F3F3F3', + thLibrarySearchbox: '#FCFCFC', + thLibraryMenuPrimary: '#3D3D3D', + thLibraryMenuSecondary: '#3D3D3D', + thLibraryMenuUnselected: '#898989', + thLibrarySelectionColor: '#FFEA9F', thTextContrast: '#1E1E1E', thTextContrast2: '#3D3D3D', @@ -263,6 +268,11 @@ const darkThemeSpec = { thBackgroundContrast: '#000000', thLeftMenuBackground: '#1D1D1D', thLibraryBackground: '#333333', + thLibrarySearchbox: '#3D3D3D', + thLibraryMenuPrimary: '#EBEBEB', + thLibraryMenuSecondary: '#EBEBEB', + thLibraryMenuUnselected: '#898989', + thLibrarySelectionColor: '#3D3D3D', thTextContrast: '#FFFFFF', thTextContrast2: '#EBEBEB', From 5ee02a1b38ff8010e15150e9e2a43a2cb00d3e4d Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 12 Jun 2023 13:57:42 +0800 Subject: [PATCH 34/38] More colors --- packages/web/components/elements/Button.tsx | 2 +- .../templates/homeFeed/LibraryFilterMenu.tsx | 60 ------------------- 2 files changed, 1 insertion(+), 61 deletions(-) diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx index dd7e39954..e405a5995 100644 --- a/packages/web/components/elements/Button.tsx +++ b/packages/web/components/elements/Button.tsx @@ -312,7 +312,7 @@ export const IconButton = styled(Button, { height: '28px', color: '#898989', border: 'unset', - background: '$thBackground', + background: 'transparent', boxSizing: 'border-box', borderRadius: 6, }, diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx index f836895c4..3cc715600 100644 --- a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx +++ b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx @@ -448,66 +448,6 @@ function LabelButton(props: LabelButtonProps): JSX.Element { ) } -type AddLinkButtonProps = { - showAddLinkModal: () => void -} - -function AddLinkButton(props: AddLinkButtonProps): JSX.Element { - const currentTheme = currentThemeName() - const isDark = currentTheme == 'Dark' - - return ( - <> - - - - - ) -} - type ViewAllButtonProps = { state: boolean setState: (state: boolean) => void From 623820f6e37ac1eb5b0fa915846a8e7dc2957e43 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 12 Jun 2023 13:59:09 +0800 Subject: [PATCH 35/38] In highlight mode we are going to always show the header --- .../templates/homeFeed/LibraryHeader.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index 24fe0999b..f922de1c5 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -40,6 +40,8 @@ type LibraryHeaderProps = { searchTerm: string | undefined applySearchQuery: (searchQuery: string) => void + alwaysShowHeader: boolean + showFilterMenu: boolean setShowFilterMenu: (show: boolean) => void @@ -56,11 +58,17 @@ export function LibraryHeader(props: LibraryHeaderProps): JSX.Element { const [showBackground, setShowBackground] = useState(false) useEffect(() => { - setShowBackground(window.scrollY > 5 || props.multiSelectMode !== 'off') - }, [props.multiSelectMode]) + if (!props.alwaysShowHeader) { + setShowBackground(window.scrollY > 5 || props.multiSelectMode !== 'off') + } else { + setShowBackground(true) + } + }, [props.multiSelectMode, props.alwaysShowHeader]) useScrollWatcher((changeset: ScrollOffsetChangeset) => { - setShowBackground(window.scrollY > 5 || props.multiSelectMode !== 'off') + if (!props.alwaysShowHeader) { + setShowBackground(window.scrollY > 5 || props.multiSelectMode !== 'off') + } }, 0) return ( From 05b8a96f33d15b1893e8df6982ec700d89e51b46 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 12 Jun 2023 14:00:04 +0800 Subject: [PATCH 36/38] Always show the header in library mode --- packages/web/components/templates/homeFeed/HomeFeedContainer.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index b60e14ec2..17d0cf3c3 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -795,6 +795,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { applySearchQuery={(searchQuery: string) => { props.applySearchQuery(searchQuery) }} + alwaysShowHeader={props.mode == 'highlights'} showFilterMenu={showFilterMenu} setShowFilterMenu={setShowFilterMenu} multiSelectMode={props.multiSelectMode} From 4cf8744ced93afb4244fbc25939551e563464191 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 12 Jun 2023 14:53:47 +0800 Subject: [PATCH 37/38] Dont display focus on the dropdown --- packages/web/components/elements/DropdownElements.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/web/components/elements/DropdownElements.tsx b/packages/web/components/elements/DropdownElements.tsx index b14f3f9a3..ed8528544 100644 --- a/packages/web/components/elements/DropdownElements.tsx +++ b/packages/web/components/elements/DropdownElements.tsx @@ -35,6 +35,9 @@ const DropdownTrigger = styled(Trigger, { '&:hover': { opacity: 0.7, }, + '&:focus': { + outline: 'none', + }, }) const StyledTriggerItem = styled(TriggerItem, { From 44207721389bc48cc456bed5624fa39990d1ef08 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 12 Jun 2023 15:09:30 +0800 Subject: [PATCH 38/38] Dont show select multiple in the highlights view --- .../templates/homeFeed/HomeFeedContainer.tsx | 1 + .../templates/homeFeed/LibraryHeader.tsx | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 17d0cf3c3..6a0ee38f3 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -795,6 +795,7 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element { applySearchQuery={(searchQuery: string) => { props.applySearchQuery(searchQuery) }} + allowSelectMultiple={props.mode !== 'highlights'} alwaysShowHeader={props.mode == 'highlights'} showFilterMenu={showFilterMenu} setShowFilterMenu={setShowFilterMenu} diff --git a/packages/web/components/templates/homeFeed/LibraryHeader.tsx b/packages/web/components/templates/homeFeed/LibraryHeader.tsx index f922de1c5..56e7e9bd2 100644 --- a/packages/web/components/templates/homeFeed/LibraryHeader.tsx +++ b/packages/web/components/templates/homeFeed/LibraryHeader.tsx @@ -41,6 +41,7 @@ type LibraryHeaderProps = { applySearchQuery: (searchQuery: string) => void alwaysShowHeader: boolean + allowSelectMultiple: boolean showFilterMenu: boolean setShowFilterMenu: (show: boolean) => void @@ -125,6 +126,7 @@ function LargeHeaderLayout(props: LibraryHeaderProps): JSX.Element { performMultiSelectAction={props.performMultiSelectAction} searchTerm={props.searchTerm} applySearchQuery={props.applySearchQuery} + allowSelectMultiple={props.allowSelectMultiple} /> ) @@ -173,6 +175,7 @@ function SmallHeaderLayout(props: LibraryHeaderProps): JSX.Element { performMultiSelectAction={props.performMultiSelectAction} searchTerm={props.searchTerm} applySearchQuery={props.applySearchQuery} + allowSelectMultiple={props.allowSelectMultiple} /> )} @@ -383,6 +386,8 @@ type ControlButtonBoxProps = { showAddLinkModal: () => void + allowSelectMultiple: boolean + numItemsSelected: number multiSelectMode: MultiSelectMode setMultiSelectMode: (mode: MultiSelectMode) => void @@ -508,9 +513,13 @@ function SearchControlButtonBox( { - props.setMultiSelectMode('none') - }} + startSelectMultiple={ + props.allowSelectMultiple + ? () => { + props.setMultiSelectMode('none') + } + : undefined + } showAddLinkModal={props.showAddLinkModal} />