Handle changing the list layout and search term using a coordinator pattern
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { Box, HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives'
|
||||
import { HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives'
|
||||
import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
|
||||
import { LibraryMenu } from './LibraryMenu'
|
||||
@ -6,19 +6,58 @@ import { LibraryAvatar } from './LibraryAvatar'
|
||||
import { LibrarySearchBar } from './LibrarySearchBar'
|
||||
import { LibraryList } from './LibraryList'
|
||||
import { LibraryHeadline } from './LibraryHeadline'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { LibraryItemsQueryInput } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
|
||||
|
||||
|
||||
|
||||
export type SearchCoordinator = {
|
||||
applySearch: (searchTerm: string) => void
|
||||
}
|
||||
|
||||
const useSearchCoordinator = () => {
|
||||
const applySearch = useCallback((searchTerm: string) => {
|
||||
|
||||
}, [])
|
||||
|
||||
return {
|
||||
applySearch
|
||||
}
|
||||
}
|
||||
|
||||
export type LibraryLayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
|
||||
export type LayoutCoordinator = {
|
||||
layout: LibraryLayoutType
|
||||
setLayout: (type: LibraryLayoutType) => void
|
||||
}
|
||||
|
||||
const useLibraryLayoutCoordinator = () => {
|
||||
const [layout, setLayout] = usePersistedState<LibraryLayoutType>({
|
||||
key: 'libraryLayout',
|
||||
initialValue: 'GRID_LAYOUT',
|
||||
})
|
||||
|
||||
return {
|
||||
layout,
|
||||
setLayout
|
||||
}
|
||||
}
|
||||
|
||||
export function LibraryContainer(): JSX.Element {
|
||||
useGetUserPreferences()
|
||||
|
||||
const { viewerData } = useGetViewerQuery()
|
||||
const searchCoordinator = useSearchCoordinator()
|
||||
const layoutCoordinator = useLibraryLayoutCoordinator()
|
||||
|
||||
return (
|
||||
<>
|
||||
<VStack alignment="start" distribution="start" css={{ width: '100vw', height: '100vh', overflow: 'hidden', bg: '$libraryBackground' }}>
|
||||
<HStack alignment="start" distribution="start" css={{ width: '100%' }}>
|
||||
<SpanBox css={{ width: '100%', height: '90px' }}>
|
||||
<LibrarySearchBar />
|
||||
<LibrarySearchBar coordinator={searchCoordinator} />
|
||||
</SpanBox>
|
||||
<SpanBox css={{ marginLeft: 'auto', width: '130px', height: '100%' }}>
|
||||
<LibraryAvatar viewer={viewerData?.me} />
|
||||
@ -27,8 +66,8 @@ export function LibraryContainer(): JSX.Element {
|
||||
<HStack alignment="start" distribution="start" css={{ width: '100%', height: '100%' }}>
|
||||
<LibraryMenu />
|
||||
<VStack alignment="start" distribution="start" css={{ width: '100%', height: '100%', mr: '20px' }}>
|
||||
<LibraryHeadline />
|
||||
<LibraryList />
|
||||
<LibraryHeadline layoutCoordinator={layoutCoordinator} />
|
||||
<LibraryList layoutCoordinator={layoutCoordinator} />
|
||||
</VStack>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
@ -5,21 +5,33 @@ import { theme } from '../../tokens/stitches.config'
|
||||
import { LibraryListLayoutIcon } from '../../elements/images/LibraryListLayoutIcon'
|
||||
import { LibraryGridLayoutIcon } from '../../elements/images/LibraryGridLayoutIcon'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { LayoutCoordinator, LibraryLayoutType } from './LibraryContainer'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export type LibraryHeadlineProps = {
|
||||
layoutCoordinator: LayoutCoordinator
|
||||
}
|
||||
|
||||
export function LibraryHeadline(): JSX.Element {
|
||||
useGetUserPreferences()
|
||||
export function LibraryHeadline(props: LibraryHeadlineProps): JSX.Element {
|
||||
|
||||
const typeColor = useCallback((type: LibraryLayoutType) => {
|
||||
return (
|
||||
props.layoutCoordinator.layout === type
|
||||
? theme.colors.omnivoreCtaYellow.toString()
|
||||
: "#D6D6D6"
|
||||
)
|
||||
}, [props.layoutCoordinator.layout])
|
||||
|
||||
return (
|
||||
<HStack alignment="center" distribution="start" css={{ pt: '4px', pb: '8px', width: '100%', pr: '15px' }}>
|
||||
<StyledText style="libraryHeader">Home</StyledText>
|
||||
<HStack alignment="center" distribution="start" css={{ marginLeft: 'auto', gap: '16px' }}>
|
||||
<Button style="ctaDarkYellow">Add Link</Button>
|
||||
<Button style="ghost">
|
||||
<LibraryListLayoutIcon color="#D6D6D6" />
|
||||
<Button style="ghost" onClick={() => props.layoutCoordinator.setLayout('LIST_LAYOUT')}>
|
||||
<LibraryListLayoutIcon color={typeColor('LIST_LAYOUT')} />
|
||||
</Button>
|
||||
<Button style="ghost">
|
||||
<LibraryGridLayoutIcon color={theme.colors.omnivoreCtaYellow.toString()} />
|
||||
<Button style="ghost" onClick={() => props.layoutCoordinator.setLayout('GRID_LAYOUT')}>
|
||||
<LibraryGridLayoutIcon color={typeColor('GRID_LAYOUT')} />
|
||||
</Button>
|
||||
</HStack>
|
||||
</HStack>
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
import { Box } from '../../elements/LayoutPrimitives'
|
||||
import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes'
|
||||
import { LibraryGridCard } from '../../patterns/LibraryCards/LibraryGridCard'
|
||||
import { LayoutCoordinator } from './LibraryContainer'
|
||||
import { EmptyLibrary } from '../homeFeed/EmptyLibrary'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
export type LibraryListProps = {
|
||||
layoutCoordinator: LayoutCoordinator
|
||||
}
|
||||
|
||||
|
||||
export function LibraryList(): JSX.Element {
|
||||
export function LibraryList(props: LibraryListProps): JSX.Element {
|
||||
useGetUserPreferences()
|
||||
|
||||
const [layout, setLayout] = useState<LayoutType>('GRID_LAYOUT')
|
||||
const { viewerData } = useGetViewerQuery()
|
||||
|
||||
|
||||
const defaultQuery = {
|
||||
limit: 50,
|
||||
sortDescending: true,
|
||||
@ -33,40 +34,43 @@ export function LibraryList(): JSX.Element {
|
||||
return items
|
||||
}, [itemsPages, performActionOnItem])
|
||||
|
||||
if (!isValidating && libraryItems.length == 0) {
|
||||
return (
|
||||
<EmptyLibrary
|
||||
onAddLinkClicked={() => {
|
||||
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box css={{ overflowY: 'scroll' }}>
|
||||
{/* // {!isValidating && items.length == 0 ? (
|
||||
// <EmptyLibrary
|
||||
// onAddLinkClicked={() => {
|
||||
|
||||
// }}
|
||||
// />
|
||||
// ) : ( */}
|
||||
<Box
|
||||
css={{
|
||||
display: 'grid',
|
||||
gridGap: layout == 'LIST_LAYOUT' ? '0' : '16px',
|
||||
width: '100%',
|
||||
|
||||
display: 'grid',
|
||||
gridAutoRows: 'auto',
|
||||
borderRadius: '8px',
|
||||
marginBottom: '0px',
|
||||
paddingRight: '14px',
|
||||
paddingTop: '0px',
|
||||
marginTop: layout == 'LIST_LAYOUT' ? '21px' : '0',
|
||||
paddingBottom: layout == 'LIST_LAYOUT' ? '0px' : '21px',
|
||||
marginTop: '0px',
|
||||
paddingBottom: '21px',
|
||||
'@smDown': {
|
||||
border: 'unset',
|
||||
width: layout == 'LIST_LAYOUT' ? '100vw' : undefined,
|
||||
margin: layout == 'LIST_LAYOUT' ? '16px -16px' : undefined,
|
||||
borderRadius: layout == 'LIST_LAYOUT' ? 0 : undefined,
|
||||
width: props.layoutCoordinator.layout == 'LIST_LAYOUT' ? '100vw' : undefined,
|
||||
margin: props.layoutCoordinator.layout == 'LIST_LAYOUT' ? '16px -16px' : undefined,
|
||||
borderRadius: props.layoutCoordinator.layout == 'LIST_LAYOUT' ? 0 : undefined,
|
||||
},
|
||||
'@lg': {
|
||||
gridTemplateColumns:
|
||||
layout == 'LIST_LAYOUT' ? 'none' : '1fr 1fr',
|
||||
props.layoutCoordinator.layout == 'LIST_LAYOUT' ? 'none' : '1fr 1fr',
|
||||
},
|
||||
'@xl': {
|
||||
gridTemplateColumns:
|
||||
layout == 'LIST_LAYOUT' ? 'none' : 'repeat(3, 1fr)',
|
||||
props.layoutCoordinator.layout == 'LIST_LAYOUT' ? 'none' : 'repeat(3, 1fr)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
@ -96,7 +100,7 @@ export function LibraryList(): JSX.Element {
|
||||
>
|
||||
{viewerData?.me && (
|
||||
<LibraryGridCard
|
||||
layout={layout}
|
||||
layout={props.layoutCoordinator.layout}
|
||||
item={linkedItem.node}
|
||||
viewer={viewerData.me}
|
||||
handleAction={(action: LinkedItemCardAction) => {
|
||||
|
||||
@ -1,26 +1,22 @@
|
||||
import { Box, HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives'
|
||||
import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
|
||||
import { HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives'
|
||||
import { useState } from 'react'
|
||||
import { FormInput } from '../../elements/FormElements'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { X } from 'phosphor-react'
|
||||
import { Sliders, SlidersHorizontal, X } from 'phosphor-react'
|
||||
import { theme } from '../../tokens/stitches.config'
|
||||
import { SearchCoordinator } from './LibraryContainer'
|
||||
|
||||
export type LibrarySearchBarProps = {
|
||||
coordinator: SearchCoordinator
|
||||
}
|
||||
|
||||
export function LibrarySearchBar(): JSX.Element {
|
||||
useGetUserPreferences()
|
||||
|
||||
|
||||
const router = useRouter()
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const { viewerData } = useGetViewerQuery()
|
||||
export function LibrarySearchBar(props: LibrarySearchBarProps): JSX.Element {
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
|
||||
return (
|
||||
<>
|
||||
<VStack alignment="start" distribution="start" css={{ pl: '32px', width: '100%', height: '100%' }}>
|
||||
<HStack alignment="start" distribution="start" css={{ width: '100%' }}>
|
||||
<HStack alignment="start" distribution="start" css={{ width: '100%', borderBottom: 'solid 1px $grayBorder' }}>
|
||||
<form
|
||||
style={{ width: '100%' }}
|
||||
onSubmit={(event) => {
|
||||
@ -33,24 +29,34 @@ export function LibrarySearchBar(): JSX.Element {
|
||||
css={{
|
||||
width: '100%',
|
||||
height: '80px',
|
||||
fontFamily: 'Inter',
|
||||
fontSize: '24px',
|
||||
fontFamily: 'Inter',
|
||||
}}
|
||||
type="text"
|
||||
tabIndex={0}
|
||||
value={searchTerm}
|
||||
placeholder="Search"
|
||||
// onFocus={(event) => {
|
||||
// event.target.select()
|
||||
// setFocused(true)
|
||||
// }}
|
||||
// onBlur={() => {
|
||||
// setFocused(false)
|
||||
// }}
|
||||
// onChange={(event) => {
|
||||
// setSearchTerm(event.target.value)
|
||||
// }}
|
||||
onChange={(event) => {
|
||||
setSearchTerm(event.target.value)
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
{!searchTerm && (
|
||||
<Button
|
||||
style="plainIcon"
|
||||
onClick={(event) => {
|
||||
// Display the advanced search sheet
|
||||
}}
|
||||
css={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Sliders size={24} color={theme.colors.utilityTextDefault.toString()} />
|
||||
</Button>
|
||||
)}
|
||||
{searchTerm && (
|
||||
<HStack alignment="center" distribution="start" css={{ height: '100%' }}>
|
||||
<Button
|
||||
@ -90,7 +96,6 @@ export function LibrarySearchBar(): JSX.Element {
|
||||
</HStack>
|
||||
)}
|
||||
</HStack>
|
||||
<SpanBox css={{ width: '100%', height: '1px', bg: '$grayBorder' }} />
|
||||
</VStack>
|
||||
</>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user