More work on the new design

This commit is contained in:
Jackson Harper
2023-02-24 19:26:32 +08:00
parent b8a42439d7
commit 5237c35a81
5 changed files with 272 additions and 96 deletions

View File

@ -15,34 +15,24 @@ export function GridSelectorIcon(props: GridSelectorIconProps): JSX.Element {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_1646_7379)">
<g>
<path
d="M8.32487 0.170105H1.6582C0.967847 0.170105 0.408203 0.729749 0.408203 1.4201V8.08677C0.408203 8.77713 0.967847 9.33677 1.6582 9.33677H8.32487C9.01523 9.33677 9.57487 8.77713 9.57487 8.08677V1.4201C9.57487 0.729749 9.01523 0.170105 8.32487 0.170105Z"
fill="#FFEA9F"
fill={fillColor}
/>
<path
d="M19.1582 0.170105H12.4915C11.8012 0.170105 11.2415 0.729749 11.2415 1.4201V8.08677C11.2415 8.77713 11.8012 9.33677 12.4915 9.33677H19.1582C19.8486 9.33677 20.4082 8.77713 20.4082 8.08677V1.4201C20.4082 0.729749 19.8486 0.170105 19.1582 0.170105Z"
fill="#FFEA9F"
fill={fillColor}
/>
<path
d="M8.32487 11.0034H1.6582C0.967847 11.0034 0.408203 11.5631 0.408203 12.2534V18.9201C0.408203 19.6105 0.967847 20.1701 1.6582 20.1701H8.32487C9.01523 20.1701 9.57487 19.6105 9.57487 18.9201V12.2534C9.57487 11.5631 9.01523 11.0034 8.32487 11.0034Z"
fill="#FFEA9F"
fill={fillColor}
/>
<path
d="M19.1582 11.0034H12.4915C11.8012 11.0034 11.2415 11.5631 11.2415 12.2534V18.9201C11.2415 19.6105 11.8012 20.1701 12.4915 20.1701H19.1582C19.8486 20.1701 20.4082 19.6105 20.4082 18.9201V12.2534C20.4082 11.5631 19.8486 11.0034 19.1582 11.0034Z"
fill="#FFEA9F"
fill={fillColor}
/>
</g>
<defs>
<clipPath id="clip0_1646_7379">
<rect
width="20"
height="20"
fill="white"
transform="translate(0.408203 0.172607)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@ -15,30 +15,20 @@ export function ListSelectorIcon(props: ListSelectorIconProps): JSX.Element {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_1646_7375)">
<g>
<path
d="M19.1582 0.795105H1.6582C0.967847 0.795105 0.408203 1.35475 0.408203 2.0451V4.12844C0.408203 4.81879 0.967847 5.37844 1.6582 5.37844H19.1582C19.8486 5.37844 20.4082 4.81879 20.4082 4.12844V2.0451C20.4082 1.35475 19.8486 0.795105 19.1582 0.795105Z"
fill="#6A6968"
fill={fillColor}
/>
<path
d="M19.1582 7.87845H1.6582C0.967847 7.87845 0.408203 8.43809 0.408203 9.12845V11.2118C0.408203 11.9021 0.967847 12.4618 1.6582 12.4618H19.1582C19.8486 12.4618 20.4082 11.9021 20.4082 11.2118V9.12845C20.4082 8.43809 19.8486 7.87845 19.1582 7.87845Z"
fill="#6A6968"
fill={fillColor}
/>
<path
d="M19.1582 14.9618H1.6582C0.967847 14.9618 0.408203 15.5214 0.408203 16.2118V18.2951C0.408203 18.9855 0.967847 19.5451 1.6582 19.5451H19.1582C19.8486 19.5451 20.4082 18.9855 20.4082 18.2951V16.2118C20.4082 15.5214 19.8486 14.9618 19.1582 14.9618Z"
fill="#6A6968"
fill={fillColor}
/>
</g>
<defs>
<clipPath id="clip0_1646_7375">
<rect
width="20"
height="20"
fill="white"
transform="translate(0.408203 0.172607)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@ -578,6 +578,7 @@ export function HomeFeedContainer(): JSX.Element {
searchTerm={queryInputs.searchQuery}
gridContainerRef={gridContainerRef}
applySearchQuery={(searchQuery: string) => {
console.log('TOP LEVEL SETTING QUERY INPUTS: ', searchQuery)
setQueryInputs({
...queryInputs,
searchQuery,
@ -781,8 +782,13 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
return (
<VStack css={{ width: '100%', height: '100%' }}>
<LibraryHeader
layout={layout}
updateLayout={updateLayout}
searchTerm={props.searchTerm}
applySearchQuery={props.applySearchQuery}
applySearchQuery={(searchQuery: string) => {
console.log('searching with searchQuery: ', searchQuery)
props.applySearchQuery(searchQuery)
}}
/>
<HStack css={{ width: '100%', height: '100%' }}>
<SpanBox
@ -792,7 +798,14 @@ function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
},
}}
>
<LibraryFilterMenu />
<LibraryFilterMenu
setShowAddLinkModal={props.setShowAddLinkModal}
searchTerm={props.searchTerm}
applySearchQuery={(searchQuery: string) => {
console.log('searching with searchQuery: ', searchQuery)
props.applySearchQuery(searchQuery)
}}
/>
</SpanBox>
<VStack

View File

@ -2,6 +2,7 @@ import {
InputHTMLAttributes,
ReactNode,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
@ -31,8 +32,16 @@ import { useGetSubscriptionsQuery } from '../../../lib/networking/queries/useGet
import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { Checkbox } from '@radix-ui/react-checkbox'
import { LayoutType } from './HomeFeedContainer'
export function LibraryFilterMenu(): JSX.Element {
type LibraryFilterMenuProps = {
setShowAddLinkModal: (show: boolean) => void
searchTerm: string | undefined
applySearchQuery: (searchTerm: string) => void
}
export function LibraryFilterMenu(props: LibraryFilterMenuProps): JSX.Element {
return (
<>
<Box
@ -47,11 +56,13 @@ export function LibraryFilterMenu(): JSX.Element {
pr: '15px',
}}
>
<SavedSearches />
<Subscriptions />
<Labels />
<SavedSearches {...props} />
<Subscriptions {...props} />
<Labels {...props} />
<AddLinkButton />
<AddLinkButton
showAddLinkModal={() => props.setShowAddLinkModal(true)}
/>
</Box>
{/* This spacer pushes library content to the right of
the fixed left side menu. */}
@ -67,19 +78,45 @@ export function LibraryFilterMenu(): JSX.Element {
)
}
function SavedSearches(): JSX.Element {
function SavedSearches(props: LibraryFilterMenuProps): JSX.Element {
return (
<MenuPanel title="Saved Searches">
<FilterButton text="Inbox" selected={true} spaced={true} />
<FilterButton text="Read Later" selected={false} spaced={true} />
<FilterButton text="Today" selected={false} spaced={true} />
<FilterButton text="Archived" selected={false} spaced={true} />
<FilterButton
text="Inbox"
filterTerm="in:inbox"
spaced={true}
{...props}
/>
<FilterButton
text="Read Later"
filterTerm="in:inbox -label:Newsletter"
spaced={true}
{...props}
/>
<FilterButton
text="Highlights"
filterTerm="type:highlights"
spaced={true}
{...props}
/>
<FilterButton
text="Files"
filterTerm="type:file"
spaced={true}
{...props}
/>
<FilterButton
text="Archived"
filterTerm="in:archive"
spaced={true}
{...props}
/>
<Box css={{ height: '10px' }}></Box>
</MenuPanel>
)
}
function Subscriptions(): JSX.Element {
function Subscriptions(props: LibraryFilterMenuProps): JSX.Element {
const { subscriptions } = useGetSubscriptionsQuery()
const [viewAll, setViewAll] = useState(false)
@ -92,14 +129,21 @@ function Subscriptions(): JSX.Element {
}}
>
{subscriptions.slice(0, viewAll ? undefined : 4).map((item) => {
return <FilterButton key={item.id} text={item.name} selected={false} />
return (
<FilterButton
key={item.id}
filterTerm={`subscription:\"${item.name}\"`}
text={item.name}
{...props}
/>
)
})}
<ViewAllButton state={viewAll} setState={setViewAll} />
</MenuPanel>
)
}
function Labels(): JSX.Element {
function Labels(props: LibraryFilterMenuProps): JSX.Element {
const { labels } = useGetLabelsQuery()
const [viewAll, setViewAll] = useState(false)
@ -112,7 +156,7 @@ function Labels(): JSX.Element {
}}
>
{labels.slice(0, viewAll ? undefined : 4).map((item) => {
return <LabelButton key={item.id} label={item} state="off" />
return <LabelButton key={item.id} label={item} {...props} />
})}
<ViewAllButton state={viewAll} setState={setViewAll} />
</MenuPanel>
@ -185,10 +229,20 @@ function MenuPanel(props: MenuPanelProps): JSX.Element {
type FilterButtonProps = {
text: string
spaced?: boolean
selected: boolean
filterTerm: string
searchTerm: string | undefined
applySearchQuery: (searchTerm: string) => void
}
function FilterButton(props: FilterButtonProps): JSX.Element {
const selected = useMemo(() => {
if (props.filterTerm === '' && !props.searchTerm) {
return true
}
return props.searchTerm === props.filterTerm
}, [props.searchTerm, props.filterTerm])
return (
<Box
css={{
@ -197,12 +251,23 @@ function FilterButton(props: FilterButtonProps): JSX.Element {
mb: props.spaced ? '10px' : '0px',
width: '100%',
height: '30px',
backgroundColor: props.selected ? '#FFEA9F' : 'unset',
backgroundColor: selected ? '#FFEA9F' : 'unset',
fontSize: '16px',
fontWeight: 'regular',
color: '#3D3D3D',
verticalAlign: 'middle',
borderRadius: '3px',
cursor: 'pointer',
'&:hover': {
backgroundColor: selected ? '#FFEA9F' : '#EBEBEB',
},
'&:active': {
backgroundColor: '#FFEA9F',
},
}}
onClick={(e) => {
props.applySearchQuery(props.filterTerm)
e.preventDefault()
}}
>
{props.text}
@ -212,10 +277,21 @@ function FilterButton(props: FilterButtonProps): JSX.Element {
type LabelButtonProps = {
label: Label
state: 'on' | 'off' | 'unset'
searchTerm: string | undefined
applySearchQuery: (searchTerm: string) => void
}
function LabelButton(props: LabelButtonProps): JSX.Element {
const state = useMemo(() => {
const term = props.searchTerm ?? ''
if (term.indexOf(`label:\"${props.label.name}\"`) >= 0) {
console.log('returning true for: ', term)
return 'on'
}
console.log('returning off for: ', term)
return 'off'
}, [props.searchTerm, props.label])
return (
<HStack
css={{
@ -229,6 +305,9 @@ function LabelButton(props: LabelButtonProps): JSX.Element {
verticalAlign: 'middle',
borderRadius: '3px',
m: '0px',
'&:hover': {
backgroundColor: '#EBEBEB',
},
}}
alignment="center"
distribution="start"
@ -236,13 +315,36 @@ function LabelButton(props: LabelButtonProps): JSX.Element {
<Circle size={9} color={props.label.color} weight="fill" />
<SpanBox css={{ pl: '10px' }}>{props.label.name}</SpanBox>
<SpanBox css={{ ml: 'auto' }}>
<input type="checkbox" />
<input
type="checkbox"
checked={state === 'on'}
onChange={(e) => {
console.log('changing check state')
if (e.target.checked) {
props.applySearchQuery
props.applySearchQuery(
`${props.searchTerm} label:\"${props.label.name}\"`
)
} else {
const query =
props.searchTerm?.replace(
`label:\"${props.label.name}\"`,
''
) ?? ''
props.applySearchQuery(query)
}
}}
/>
</SpanBox>
</HStack>
)
}
function AddLinkButton(): JSX.Element {
type AddLinkButtonProps = {
showAddLinkModal: () => void
}
function AddLinkButton(props: AddLinkButtonProps): JSX.Element {
return (
<VStack
css={{
@ -266,6 +368,10 @@ function AddLinkButton(): JSX.Element {
alignItems: 'center',
fontWeight: '600',
}}
onClick={(e) => {
props.showAddLinkModal()
e.preventDefault()
}}
>
<Plus size={16} weight="bold" />
<SpanBox css={{ width: '10px' }}></SpanBox>Add Link

View File

@ -1,6 +1,7 @@
import {
InputHTMLAttributes,
ReactNode,
useCallback,
useEffect,
useRef,
useState,
@ -8,7 +9,7 @@ import {
import { StyledText } from '../../elements/StyledText'
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { SearchIcon } from '../../elements/images/SearchIcon'
import { theme } from '../../tokens/stitches.config'
import { theme, ThemeId } from '../../tokens/stitches.config'
import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
import { FormInput } from '../../elements/FormElements'
import { searchBarCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
@ -20,48 +21,25 @@ import { OmnivoreFullLogo } from '../../elements/images/OmnivoreFullLogo'
import { AvatarDropdown } from '../../elements/AvatarDropdown'
import { ListSelectorIcon } from '../../elements/images/ListSelectorIcon'
import { GridSelectorIcon } from '../../elements/images/GridSelectorIcon'
import { LayoutType } from './HomeFeedContainer'
import { DropdownMenu, HeaderDropdownAction } from '../../patterns/DropdownMenu'
import { updateTheme } from '../../../lib/themeUpdater'
import { useRouter } from 'next/router'
type LibrarySearchBarProps = {
searchTerm?: string
type LibraryHeaderProps = {
layout: LayoutType
updateLayout: (layout: LayoutType) => void
searchTerm: string | undefined
applySearchQuery: (searchQuery: string) => void
}
type LibraryFilter =
| 'in:inbox'
| 'in:all'
| 'in:archive'
| 'type:file'
| 'type:highlights'
| `saved:${string}`
| `sort:read`
// get last week's date
const recentlySavedStartDate = new Date(
new Date().getTime() - 7 * 24 * 60 * 60 * 1000
).toLocaleDateString('en-US')
const FOCUSED_BOXSHADOW = '0px 0px 2px 2px rgba(255, 234, 159, 0.56)'
const HEADER_HEIGHT = '105px'
const MOBILE_HEIGHT = '44px'
export function LibraryHeader(props: LibrarySearchBarProps): JSX.Element {
const [focused, setFocused] = useState(false)
const inputRef = useRef<HTMLInputElement>(null)
const [searchTerm, setSearchTerm] = useState(props.searchTerm || '')
useEffect(() => {
setSearchTerm(props.searchTerm || '')
}, [props.searchTerm])
useKeyboardShortcuts(
searchBarCommands((action) => {
if (action === 'focusSearchBar' && inputRef.current) {
inputRef.current.select()
}
})
)
const MOBILE_HEIGHT = '48px'
export function LibraryHeader(props: LibraryHeaderProps): JSX.Element {
return (
<>
<VStack
@ -93,7 +71,10 @@ export function LibraryHeader(props: LibrarySearchBarProps): JSX.Element {
>
<LogoBox />
<SearchBox {...props} />
<ControlButtonBox />
<ControlButtonBox
layout={props.layout}
updateLayout={props.updateLayout}
/>
</HStack>
</VStack>
{/* This spacer is put in to push library content down
@ -111,10 +92,23 @@ export function LibraryHeader(props: LibrarySearchBarProps): JSX.Element {
)
}
function SearchBox(props: LibrarySearchBarProps): JSX.Element {
type SearchBoxProps = {
searchTerm: string | undefined
applySearchQuery: (searchQuery: string) => void
}
function SearchBox(props: SearchBoxProps): JSX.Element {
const inputRef = useRef<HTMLInputElement | null>(null)
const [focused, setFocused] = useState(false)
const [searchTerm, setSearchTerm] = useState('')
const [searchTerm, setSearchTerm] = useState(props.searchTerm ?? '')
useKeyboardShortcuts(
searchBarCommands((action) => {
if (action === 'focusSearchBar' && inputRef.current) {
inputRef.current.select()
}
})
)
return (
<Box
@ -128,6 +122,7 @@ function SearchBox(props: LibrarySearchBarProps): JSX.Element {
'@mdDown': {
display: 'none',
},
boxShadow: focused ? FOCUSED_BOXSHADOW : 'unset',
}}
>
<HStack
@ -139,6 +134,10 @@ function SearchBox(props: LibrarySearchBarProps): JSX.Element {
alignment="center"
distribution="start"
css={{ height: '100%', px: '15px' }}
onClick={(e) => {
inputRef.current?.focus()
e.preventDefault()
}}
>
<MagnifyingGlass
size={20}
@ -151,6 +150,7 @@ function SearchBox(props: LibrarySearchBarProps): JSX.Element {
props.applySearchQuery(searchTerm || '')
inputRef.current?.blur()
}}
style={{ width: '100%' }}
>
<FormInput
ref={inputRef}
@ -169,7 +169,7 @@ function SearchBox(props: LibrarySearchBarProps): JSX.Element {
}}
/>
</form>
{searchTerm ? (
{props.searchTerm ? (
<Button
style="plainIcon"
onClick={(event) => {
@ -206,7 +206,9 @@ function SearchBox(props: LibrarySearchBarProps): JSX.Element {
height: '28px',
color: '#898989',
}}
// onClick={() => requestAnimationFrame(() => inputRef.current.focus())}
onClick={() =>
requestAnimationFrame(() => inputRef?.current?.focus())
}
// we can make it unreachable via keyboard as we have the same message for the SR label
tabIndex={-1}
>
@ -240,8 +242,6 @@ function LogoBox(): JSX.Element {
css={{
ml: '20px',
mr: '20px',
height: '22px',
width: '22px',
'@md': {
display: 'none',
},
@ -253,7 +253,57 @@ function LogoBox(): JSX.Element {
)
}
function ControlButtonBox(): JSX.Element {
type ControlButtonBoxProps = {
layout: LayoutType
updateLayout: (layout: LayoutType) => void
}
function ControlButtonBox(props: ControlButtonBoxProps): JSX.Element {
const router = useRouter()
const headerDropdownActionHandler = useCallback(
(action: HeaderDropdownAction) => {
switch (action) {
case 'apply-darker-theme':
updateTheme(ThemeId.Darker)
break
case 'apply-dark-theme':
updateTheme(ThemeId.Dark)
break
case 'apply-lighter-theme':
updateTheme(ThemeId.Lighter)
break
case 'apply-light-theme':
updateTheme(ThemeId.Light)
break
case 'navigate-to-install':
router.push('/settings/installation')
break
case 'navigate-to-emails':
router.push('/settings/emails')
break
case 'navigate-to-labels':
router.push('/settings/labels')
break
case 'navigate-to-subscriptions':
router.push('/settings/subscriptions')
break
case 'navigate-to-api':
router.push('/settings/api')
break
case 'navigate-to-integrations':
router.push('/settings/integrations')
break
case 'logout':
// props.setShowLogoutConfirmation(true)
break
default:
break
}
},
[updateTheme, router]
)
return (
<>
<HStack
@ -271,9 +321,36 @@ function ControlButtonBox(): JSX.Element {
},
}}
>
<ListSelectorIcon />
<GridSelectorIcon />
<AvatarDropdown userInitials="JH" />
<Button
style="plainIcon"
css={{ display: 'flex' }}
onClick={(e) => {
props.updateLayout('LIST_LAYOUT')
e.preventDefault()
}}
>
<ListSelectorIcon
color={props.layout == 'GRID_LAYOUT' ? '#6A6968' : '#FFEA9F'}
/>
</Button>
<Button
style="plainIcon"
css={{ display: 'flex' }}
onClick={(e) => {
props.updateLayout('GRID_LAYOUT')
e.preventDefault()
}}
>
<GridSelectorIcon
color={props.layout == 'LIST_LAYOUT' ? '#6A6968' : '#FFEA9F'}
/>
</Button>
<DropdownMenu
username={'props.username'}
triggerElement={<AvatarDropdown userInitials="JH" />}
actionHandler={headerDropdownActionHandler}
/>
</HStack>
<HStack